Commit 944b11de by chenxianqi

init product

parents
Showing with 3501 additions and 0 deletions
attach
mimc.log
kefu_server
kefu_server.tar.gz
\ No newline at end of file
###======install beego=======
##### go get github.com/astaxie/beego
###=====install MIMC-Go-SDK====
#####go get github.com/Xiaomi-mimc/mimc-go-sdk
#####cd $GOPATH/src/github.com/Xiaomi-mimc/mimc-go-sdk
#####go build
#####go install
###**下载依赖库**
#####MIMC-Go-SDK依赖proto buffer进行序列化与反序列化数据,在使用时,确保已经install了proto buffer。如未安装,可参考如下进行安装操作。
#####go get github.com/golang/protobuf/proto
#####// 进入下载目录
#####cd $GOPATH/src/github.com/golang/protobuf/proto
#####// 编译安装
#####go build
#####go install
###===== cache 模块 ====
go get github.com/astaxie/beego/cache
###==== 七牛云SDK ====
#####go get -u github.com/qiniu/api.v7
###=====运行====
#####bee run
#####打包发布
#####bee pack -be GOOS=linux
\ No newline at end of file
appname = kefu_server
runmode = "dev"
httpport = 8080
copyrequestbody = true
viewspath = "public"
static_host = "http://localhost:8080/static/uploads/images"
# 进程监控
EnableAdmin = false
AdminAddr = "localhost"
AdminPort = 8089
# 小米mimc open api URL
mimc_HttpUrl = "https://mimc.chat.xiaomi.net/api/account/token"
# https
#EnableHTTPS = true
#EnableHttpTLS = true
#HTTPSPort = 443
#HTTPSCertFile = "conf/ssl.crt"
#HTTPSKeyFile = "conf/ssl.key"
[dev]
httpaddr = "localhost"
# 小米mimc配置信息(小米开放平台创建)
mimc_appId = 2882303761518282099
mimc_appKey = "5521828290099"
mimc_appSecret = "516JCA60FdP9bHQUdpXK+Q=="
# IM数据库信息
im_alias_name = "default"
im_driver_name= "mysql"
im_mysql_host = "192.168.31.72"
im_mysql_user = "root"
im_mysql_db = "kefu_server"
im_mysql_pwd = "chenxianqi"
[prod]
httpaddr = "localhost"
# 小米mimc配置信息(小米开放平台创建)
mimc_appId = 2882303761518282099
mimc_appKey = "5521828290099"
mimc_appSecret = "516JCA60FdP9bHQUdpXK+Q=="
# IM数据库信息
im_alias_name = "default"
im_driver_name= "mysql"
im_mysql_host = "host"
im_mysql_user = "user"
im_mysql_db = "kefu_server"
im_mysql_pwd = "pwd"
package controllers
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"kefu_server/models"
"kefu_server/utils"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
)
// AuthController struct
type AuthController struct {
beego.Controller
}
// Login admin login
func (c *AuthController) Login() {
var admin models.Admin
valid := validation.Validation{}
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &admin); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误", nil)
c.ServeJSON()
return
}
// MD5
m5 := md5.New()
m5.Write([]byte(admin.Password))
admin.Password = hex.EncodeToString(m5.Sum(nil))
// valid
valid.Required(admin.UserName, "username").Message("用户名不能为空!")
valid.Required(admin.Password, "password").Message("密码不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
o := orm.NewOrm()
queryAdmin := models.Admin{UserName: admin.UserName}
err := o.Read(&queryAdmin, "UserName")
if err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "用户不存在!", nil)
} else if queryAdmin.Password != admin.Password {
c.Data["json"] = utils.ResponseError(c.Ctx, "密码错误!", nil)
} else if admin.UserName != queryAdmin.UserName {
c.Data["json"] = utils.ResponseError(c.Ctx, "用户不存在!", nil)
} else {
queryAdmin.Password = ""
queryAdmin.Token = ""
queryAdmin.Token = utils.GenerateToken(queryAdmin)
_, err := o.Update(&queryAdmin, "Token")
if err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "登录失败,请稍后再试!", queryAdmin)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "登录成功!", queryAdmin)
}
}
c.ServeJSON()
}
// Logout admin logout
func (c *AuthController) Logout() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
admin := models.Admin{Token: token}
_ = o.Read(&admin, "Token")
admin.Token = ""
admin.CurrentConUser = 0
if _, err := o.Update(&admin, "Token", "CurrentConUser"); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "退出失败!", nil)
c.ServeJSON()
return
}
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "退出成功!", nil)
c.ServeJSON()
}
package controllers
import (
"encoding/json"
"kefu_server/models"
"kefu_server/utils"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
)
// CompanyController struct
type CompanyController struct {
beego.Controller
}
// Get get conpany info
func (c *CompanyController) Get() {
o := orm.NewOrm()
company := models.Company{ID: 1}
if err := o.Read(&company); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败!", err)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &company)
}
c.ServeJSON()
}
// Put update conpany info
func (c *CompanyController) Put() {
company := models.Company{}
company.UpdateAt = time.Now().Unix()
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &company); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", nil)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(company.Logo, "logo").Message("公司LOGO不能为空!")
valid.Required(company.Title, "title").Message("公司名称不能为空!")
valid.MaxSize(company.Title, 50, "title").Message("公司名称不能超过50个字符!")
valid.Required(company.Service, "service").Message("在线客服时间不能为空!")
valid.MaxSize(company.Service, 50, "service").Message("在线客服时间长度不能超过50个字符!")
valid.MaxSize(company.Email, 50, "service").Message("Email长度不能超过50个字符!")
valid.MaxSize(company.Tel, 50, "tel").Message("公司电话长度不能超过50个字符!")
if valid.HasErrors() {
for _, err := range valid.Errors {
logs.Error(err)
c.Data["json"] = &models.Response{Code: 400, Message: err.Message, Data: nil}
break
}
c.ServeJSON()
return
}
// orm
o := orm.NewOrm()
company.ID = 1
company.UpdateAt = time.Now().Unix()
if _, err := o.Update(&company); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "更新失败!", err)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "更新成功!", &company)
}
c.ServeJSON()
}
package controllers
import (
"encoding/base64"
"kefu_server/models"
"kefu_server/utils"
"strconv"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
)
// ContactController struct
type ContactController struct {
beego.Controller
}
// GetContacts get all Contacts
func (c *ContactController) GetContacts() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
admin := models.Admin{Token: token}
_ = o.Read(&admin, "Token")
var contactData []models.ContactData
rCount, err := o.Raw("SELECT c.id AS cid,c.to_account,c.is_session_end, c.last_message,c.last_message_type,c.from_account, c.create_at AS contact_create_at,u.*, IFNULL(m.`count`,0) AS `read` FROM `contact` c LEFT JOIN `user` u ON c.from_account = u.id LEFT JOIN (SELECT to_account,from_account, COUNT(*) as `count` FROM message WHERE `read` = 1 GROUP BY to_account,from_account) m ON m.to_account = c.to_account AND m.from_account = c.from_account WHERE c.to_account = ? AND c.delete = 0 ORDER BY c.create_at DESC", admin.ID).QueryRows(&contactData)
logs.Info("contactData===", contactData)
if err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败!", err)
} else {
if rCount == 0 {
contactData = []models.ContactData{}
}
// base 64
for index, contact := range contactData {
payload, _ := base64.StdEncoding.DecodeString(contact.LastMessage)
contactData[index].LastMessage = string(payload)
}
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &contactData)
}
c.ServeJSON()
}
// Delete a Contact
func (c *ContactController) Delete() {
o := orm.NewOrm()
id, _ := strconv.ParseInt(c.Ctx.Input.Param(":id"), 10, 64)
token := c.Ctx.Input.Header("Authorization")
admin := models.Admin{Token: token}
_ = o.Read(&admin, "Token")
res, _ := o.Raw("UPDATE `contact` SET `delete` = 1 WHERE id = ? AND to_account = ?", id, admin.ID).Exec()
if rowsAffected, _ := res.RowsAffected(); rowsAffected == 0 {
c.Data["json"] = utils.ResponseError(c.Ctx, "删除失败!", nil)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "删除成功!", nil)
}
c.ServeJSON()
}
// Clear all
func (c *ContactController) Clear() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
admin := models.Admin{Token: token}
_ = o.Read(&admin, "Token")
res, _ := o.Raw("UPDATE `contact` SET `delete` = 1 WHERE to_account = ?", admin.ID).Exec()
if rowsAffected, _ := res.RowsAffected(); rowsAffected == 0 {
c.Data["json"] = utils.ResponseError(c.Ctx, "清空失败!", nil)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "清空成功!", nil)
}
c.ServeJSON()
}
package controllers
import (
"github.com/astaxie/beego"
)
// ErrorController struct
type ErrorController struct {
beego.Controller
}
// Error404 Controller public fun
func (c *ErrorController) Error404() {
c.Data["content"] = "page not found"
c.TplName = "404.tpl"
}
package controllers
import (
"encoding/json"
"kefu_server/models"
"kefu_server/utils"
"math"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
)
// HomeController struct
type HomeController struct {
beego.Controller
}
// StatisticalRequest home Statistical
type StatisticalRequest struct {
DateStart string `json:"date_start"`
DateEnd string `json:"date_end"`
}
// Statistical statistical services
func (c *HomeController) Statistical() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
admin := models.Admin{Token: token}
if err := o.Read(&admin, "Token"); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败,用户不存在", err)
c.ServeJSON()
return
}
// request body
statisticalRequest := StatisticalRequest{}
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &statisticalRequest); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", nil)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(statisticalRequest.DateStart, "date_start").Message("date_start不能为空!")
valid.Required(statisticalRequest.DateEnd, "date_end").Message("date_end不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
// transform date
var oneDaySecond float64 = 86400
layoutDate := "2006-01-02 15:04:05"
loc, _ := time.LoadLocation("Local")
dateStartString := statisticalRequest.DateStart + " 00:00:00"
dateEndString := statisticalRequest.DateEnd + " 23:59:59"
dateStart, _ := time.ParseInLocation(layoutDate, dateStartString, loc)
dateEnd, _ := time.ParseInLocation(layoutDate, dateEndString, loc)
k := dateEnd.Unix() - dateStart.Unix()
maxDay := int(math.Ceil(float64(k) / oneDaySecond))
if maxDay < 1 || maxDay >= 32 {
c.Data["json"] = utils.ResponseError(c.Ctx, "日期有误,最大只能查询一个月以内", nil)
c.ServeJSON()
return
}
countsArr := map[string]interface{}{}
// Count customer service access
var membersData []orm.Params
_, _ = o.Raw("SELECT a.id, a.username, a.nickname, IFNULL(s.count,0) as count FROM `admin` a LEFT JOIN (SELECT service_account,COUNT(*) AS count FROM services_statistical WHERE `create_at` BETWEEN ? AND ? GROUP BY service_account) s ON a.id = s.service_account ORDER BY a.id", dateStart.Unix(), dateEnd.Unix()).Values(&membersData)
countsArr["members"] = membersData
// Count the traffic of each channel
var statisticalData []interface{}
for i := 0; i < maxDay; i++ {
var statisticalTemp []orm.Params
increment := int64(i) * int64(oneDaySecond)
start := dateStart.Unix() + increment
end := start + int64(oneDaySecond)
if i == maxDay-1 {
end = dateEnd.Unix()
}
_, _ = o.Raw("SELECT p.id, p.title, IFNULL(s.count,0) as count FROM `platform` p LEFT JOIN (SELECT platform,COUNT(*) AS count FROM `services_statistical` WHERE `create_at` BETWEEN ? AND ? GROUP BY platform) s ON p.id = s.platform ORDER BY p.id", start, end).Values(&statisticalTemp)
day := time.Unix(start, 0).Format("2006-01-02")
statisticalArrItem := map[string]interface{}{}
statisticalArrItem["date"] = day
statisticalArrItem["list"] = statisticalTemp
statisticalData = append(statisticalData, statisticalArrItem)
}
countsArr["statistical"] = statisticalData
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &countsArr)
c.ServeJSON()
}
// TodayActionStatistical today Statistical
func (c *HomeController) TodayActionStatistical() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
admin := models.Admin{Token: token}
if err := o.Read(&admin, "Token"); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败,用户不存在", err)
c.ServeJSON()
return
}
// request body
statisticalRequest := StatisticalRequest{}
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &statisticalRequest); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", nil)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(statisticalRequest.DateStart, "date_start").Message("date_start不能为空!")
valid.Required(statisticalRequest.DateEnd, "date_end").Message("date_end不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
// transform date
layoutDate := "2006-01-02 15:04:05"
loc, _ := time.LoadLocation("Local")
dateStartString := statisticalRequest.DateStart + " 00:00:00"
dateEndString := statisticalRequest.DateEnd + " 23:59:59"
dateStart, _ := time.ParseInLocation(layoutDate, dateStartString, loc)
dateEnd, _ := time.ParseInLocation(layoutDate, dateEndString, loc)
var statisticalData []orm.Params
_, _ = o.Raw("SELECT p.id platform,p.title, IFNULL(u.count,0) AS `count` FROM platform as p LEFT JOIN (SELECT platform,COUNT(*) AS count FROM `user` WHERE last_activity BETWEEN ? AND ? GROUP BY platform) u ON p.id = u.platform", dateStart.Unix(), dateEnd.Unix()).Values(&statisticalData)
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &statisticalData)
c.ServeJSON()
}
package controllers
import (
"encoding/json"
"kefu_server/models"
"kefu_server/utils"
"strconv"
"strings"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
)
// KnowledgeBaseController struct
type KnowledgeBaseController struct {
beego.Controller
}
// Get get a knowledge Base
func (c *KnowledgeBaseController) Get() {
o := orm.NewOrm()
id, _ := strconv.ParseInt(c.Ctx.Input.Param(":id"), 10, 64)
knowledgeBase := models.KnowledgeBase{ID: id}
if err := o.Read(&knowledgeBase); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "获取失败!", err)
c.ServeJSON()
}
knowledgeBase.SubTitle = strings.Trim(knowledgeBase.SubTitle, "|")
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &knowledgeBase)
c.ServeJSON()
}
// Post add a knowledge Base
func (c *KnowledgeBaseController) Post() {
o := orm.NewOrm()
// request body
var knowledgeBase models.KnowledgeBase
knowledgeBase.CreateAt = time.Now().Unix()
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &knowledgeBase); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", err)
c.ServeJSON()
}
// exist title ?
oldKnowledgeBase := models.KnowledgeBase{Title: knowledgeBase.Title}
if err := o.Read(&oldKnowledgeBase, "Title"); err == nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "标题已存在,请换个标题!", nil)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(knowledgeBase.Title, "title").Message("标题不能为空!")
valid.Required(knowledgeBase.Content, "content").Message("内容不能为空!")
valid.Required(knowledgeBase.UID, "uid").Message("用户ID不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
// Platform exist
if err := o.Read(&models.Platform{ID: knowledgeBase.Platform}); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "不存在的平台id!", nil)
c.ServeJSON()
return
}
// insert
knowledgeBase.SubTitle = strings.Trim(knowledgeBase.SubTitle, "|")
if knowledgeBase.SubTitle != "" {
knowledgeBase.SubTitle = "|" + knowledgeBase.SubTitle + "|"
}
if id, err := o.Insert(&knowledgeBase); err == nil {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "添加成功!", &id)
} else {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "服务异常!", err)
}
c.ServeJSON()
}
// Put update a knowledge Base
func (c *KnowledgeBaseController) Put() {
o := orm.NewOrm()
// request body
var newKnowledgeBase models.KnowledgeBase
newKnowledgeBase.UpdateAt = time.Now().Unix()
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &newKnowledgeBase); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", err)
c.ServeJSON()
}
// validation
valid := validation.Validation{}
valid.Required(newKnowledgeBase.Title, "title").Message("标题不能为空!")
valid.Required(newKnowledgeBase.Content, "content").Message("内容不能为空!")
valid.Required(newKnowledgeBase.UID, "uid").Message("用户ID不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
// is exist ?
oldKnowledgeBase := models.KnowledgeBase{ID: newKnowledgeBase.ID}
if err := o.Read(&oldKnowledgeBase); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "内容不存在!", err)
c.ServeJSON()
}
// is exist title?
oldKnowledgeBase = models.KnowledgeBase{Title: newKnowledgeBase.Title}
if err := o.Read(&oldKnowledgeBase, "Title"); err == nil && oldKnowledgeBase.ID != newKnowledgeBase.ID {
c.Data["json"] = utils.ResponseError(c.Ctx, "标题已存在,请换个标题!", nil)
c.ServeJSON()
}
// is exist Platform?
if err := o.Read(&models.Platform{ID: newKnowledgeBase.Platform}); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "不存在的平台id!", nil)
c.ServeJSON()
return
}
// sub title
newKnowledgeBase.SubTitle = strings.Trim(newKnowledgeBase.SubTitle, "|")
if newKnowledgeBase.SubTitle != "" {
newKnowledgeBase.SubTitle = "|" + newKnowledgeBase.SubTitle + "|"
}
// insert
newKnowledgeBase.CreateAt = oldKnowledgeBase.CreateAt
if _, err := o.Update(&newKnowledgeBase, "Title", "SubTitle", "Content", "Platform", "UpdateAt"); err == nil {
newKnowledgeBase.SubTitle = strings.Trim(newKnowledgeBase.SubTitle, "|")
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "更新成功!", &newKnowledgeBase)
} else {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "更新失败!", err)
}
c.ServeJSON()
}
// Delete delete a knowledge Base
func (c *KnowledgeBaseController) Delete() {
o := orm.NewOrm()
id, _ := strconv.ParseInt(c.Ctx.Input.Param(":id"), 10, 64)
knowledgeBase := models.KnowledgeBase{ID: id}
// exist
if err := o.Read(&knowledgeBase); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "删除失败,内容不存在!", err)
c.ServeJSON()
return
}
if num, err := o.Delete(&knowledgeBase); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "删除失败!", nil)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "删除成功!", num)
}
c.ServeJSON()
}
// List quesy list
func (c *KnowledgeBaseController) List() {
// request body
var paginationData models.PaginationData
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &paginationData); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", err)
c.ServeJSON()
return
}
// orm instance
o := orm.NewOrm()
model := new(models.KnowledgeBase)
qs := o.QueryTable(model)
// query
var lists []models.KnowledgeBase
if _, err := qs.OrderBy("-create_at").Limit(paginationData.PageSize).Offset((paginationData.PageOn - 1) * paginationData.PageSize).All(&lists); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败!", err)
c.ServeJSON()
return
}
total, _ := qs.Count()
for index := range lists {
lists[index].SubTitle = strings.Trim(lists[index].SubTitle, "|")
}
paginationData.Total = total
paginationData.List = &lists
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &paginationData)
c.ServeJSON()
}
package controllers
import (
"encoding/base64"
"encoding/json"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
"kefu_server/im"
"kefu_server/models"
"kefu_server/utils"
"strconv"
"time"
)
// MessageController struct
type MessageController struct {
beego.Controller
}
// MessagePaginationData struct
type MessagePaginationData struct {
PageSize int `json:"page_size"`
Total int64 `json:"total"`
Account int64 `json:"account"`
Service int64 `json:"service"`
Timestamp int64 `json:"timestamp"`
List interface{} `json:"list"`
}
// List get messages
func (c *MessageController) List() {
o := orm.NewOrm()
messagePaginationData := MessagePaginationData{}
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &messagePaginationData); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", nil)
c.ServeJSON()
return
}
// service ID
var serviceID int64
if messagePaginationData.Service == 0 {
token := c.Ctx.Input.Header("Authorization")
admin := models.Admin{Token: token}
if err := o.Read(&admin, "Token"); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败,用户不存在", err)
c.ServeJSON()
return
}
serviceID = admin.ID
} else {
serviceID = messagePaginationData.Service
}
// Timestamp == 0
if messagePaginationData.Timestamp == 0 {
messagePaginationData.Timestamp = time.Now().Unix()
}
// validation
valid := validation.Validation{}
valid.Required(messagePaginationData.Account, "account").Message("account不能为空!")
valid.Required(messagePaginationData.PageSize, "page_size").Message("page_size不能为空!")
valid.Required(messagePaginationData.Timestamp, "timestamp").Message("timestamp不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
// join string
qs := o.QueryTable(new(models.Message))
accounts := []int64{messagePaginationData.Account, serviceID}
inExp := "?,?"
// get all robot
robots := im.GetRobots()
for _, robot := range robots {
accounts = append(accounts, robot.ID)
inExp = inExp + ",?"
}
var messages []*models.Message
msgCount, _ := qs.Filter("timestamp__lt", messagePaginationData.Timestamp).Filter("to_account__in", accounts).Filter("from_account__in", accounts).Filter("delete", 0).Count()
// Paging
end := msgCount
start := int(msgCount) - messagePaginationData.PageSize
if start <= 0 {
start = 0
}
if msgCount > 0 {
_, err := o.Raw("SELECT * FROM `message` WHERE to_account IN ("+inExp+") AND `delete` = 0 AND from_account IN ("+inExp+") AND `timestamp` < ? ORDER BY `timestamp` ASC LIMIT ?,?", accounts, accounts, messagePaginationData.Timestamp, start, end).QueryRows(&messages)
_, _ = qs.Filter("from_account", messagePaginationData.Account).Update(orm.Params{"read": 0})
if err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败!", &err)
c.ServeJSON()
return
}
total, _ := qs.Filter("to_account__in", accounts).Filter("from_account__in", accounts).Count()
messagePaginationData.List = messages
messagePaginationData.Total = total
} else {
messagePaginationData.List = []models.Message{}
messagePaginationData.Total = 0
}
for index, msg := range messages {
payload, _ := base64.StdEncoding.DecodeString(msg.Payload)
messages[index].Payload = string(payload)
}
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &messagePaginationData)
c.ServeJSON()
}
// TransferRequestData struct
type TransferRequestData struct {
ToAccount int64 `json:"to_account"` // 转接给谁
UserAccount int64 `json:"user_account"` // 用户ID
}
// Transfer transfer user to user
func (c *MessageController) Transfer() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
admin := models.Admin{Token: token}
if err := o.Read(&admin, "Token"); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败,用户不存在", err)
c.ServeJSON()
return
}
// request body
transferRequestData := TransferRequestData{}
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &transferRequestData); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", nil)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(transferRequestData.ToAccount, "to_account").Message("to_account不能为空!")
valid.Required(transferRequestData.UserAccount, "user_account").Message("user不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
robot := im.Robots[0]
robotID, _ := strconv.ParseInt(robot.AppAccount(), 10, 64)
type adminData struct {
ID int64 `orm:"column(id)" json:"id"`
NickName string `json:"nickname"`
Avatar string `json:"avatar"`
}
toAdmin := models.Admin{ID: transferRequestData.ToAccount}
if err := o.Read(&toAdmin); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "转接失败,转接客服不存在", nil)
c.ServeJSON()
return
}
toAdminJSON, _ := json.Marshal(adminData{ID: toAdmin.ID, Avatar: toAdmin.Avatar, NickName: toAdmin.NickName})
user := models.User{ID: transferRequestData.UserAccount}
_ = o.Read(&user)
// message
message := models.Message{}
message.BizType = "transfer"
message.FromAccount = transferRequestData.UserAccount
message.Timestamp = time.Now().Unix()
message.TransferAccount = transferRequestData.ToAccount
// Send to forwarder
message.ToAccount = admin.ID
message.Payload = "您将" + user.NickName + "转接给" + toAdmin.NickName
messageJSONOne, _ := json.Marshal(message)
messageStringOne := base64.StdEncoding.EncodeToString([]byte(messageJSONOne))
robot.SendMessage(strconv.FormatInt(admin.ID, 10), []byte(messageStringOne))
im.MessageInto(message, true)
// Send to forwarded customer service
message.ToAccount = transferRequestData.ToAccount
message.Payload = admin.NickName + "将" + user.NickName + "转接给您"
messageJSONTwo, _ := json.Marshal(message)
messageStringTwo := base64.StdEncoding.EncodeToString([]byte(messageJSONTwo))
robot.SendMessage(strconv.FormatInt(transferRequestData.ToAccount, 10), []byte(messageStringTwo))
im.MessageInto(message, true)
// send to user
message.ToAccount = transferRequestData.UserAccount
message.FromAccount = robotID
message.Delete = 1
message.Payload = string(toAdminJSON)
messageJSONThree, _ := json.Marshal(message)
messageString3 := base64.StdEncoding.EncodeToString([]byte(messageJSONThree))
robot.SendMessage(strconv.FormatInt(transferRequestData.UserAccount, 10), []byte(messageString3))
im.MessageInto(message, false)
// Transfer to the library for counting service times
servicesStatistical := models.ServicesStatistical{UserAccount: transferRequestData.UserAccount, ServiceAccount: transferRequestData.ToAccount, TransferAccount: admin.ID, Platform: user.Platform, CreateAt: time.Now().Unix()}
_, _ = o.Insert(&servicesStatistical)
// End the repeater's and user's current session
tk := time.NewTimer(1 * time.Second)
select {
case <-tk.C:
endUsersID := []int64{admin.ID, transferRequestData.UserAccount}
_, _ = o.Raw("UPDATE contact SET is_session_end = 1 WHERE to_account IN(?,?) AND from_account IN(?,?)", endUsersID, endUsersID).Exec()
}
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "转接成功", nil)
c.ServeJSON()
}
package controllers
import (
"encoding/json"
"kefu_server/models"
"kefu_server/utils"
"strconv"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
)
// PlatformController struct
type PlatformController struct {
beego.Controller
}
// Get get a admin
func (c *PlatformController) Get() {
o := orm.NewOrm()
id, _ := strconv.ParseInt(c.Ctx.Input.Param(":id"), 10, 64)
platform := models.Platform{ID: id}
if err := o.Read(&platform); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败!", err)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &platform)
}
c.ServeJSON()
}
// Put update admin
func (c *PlatformController) Put() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
// is admin ?
if _admin.Root != 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "更新失败,无权限更新", nil)
c.ServeJSON()
return
}
// request body
platform := models.Platform{}
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &platform); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", nil)
c.ServeJSON()
return
}
// admin exist
if err := o.Read(&models.Platform{ID: platform.ID}); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "更新失败,数据不存在!", err)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(platform.ID, "id").Message("平台ID不能为空!")
valid.Min(platform.ID, 7, "id").Message("默认配置平台不能修改!")
valid.Required(platform.Title, "title").Message("平台名不能为空!")
valid.MaxSize(platform.Title, 30, "title").Message("平台名不能超过30个字符!")
valid.AlphaNumeric(platform.Alias, "alias").Message("别名不能含有中文和特别符号!")
valid.Required(platform.Alias, "alias").Message("别名不能为空!")
valid.MaxSize(platform.Alias, 30, "alias").Message("别名不能超过30个字符!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
// title exist
var pt models.Platform
if err := o.Raw("SELECT * FROM platform WHERE id != ? AND title = ?", platform.ID, platform.Title).QueryRow(&pt); err == nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "平台名已被使用,请换一个试试!", err)
c.ServeJSON()
return
}
// Alias exist
if err := o.Raw("SELECT * FROM platform WHERE id != ? AND alias = ?", platform.ID, platform.Alias).QueryRow(&pt); err == nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "别名已被使用,请换一个试试!", err)
c.ServeJSON()
return
}
if _, err := o.Update(&platform, "Title", "Alias"); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "更新失败!", err)
} else {
platform.System = 0
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "更新成功!", &platform)
}
c.ServeJSON()
}
// Post add new admin
func (c *PlatformController) Post() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
if _admin.Root != 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "您没有权限添加平台!", nil)
c.ServeJSON()
return
}
// request body
var platform models.Platform
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &platform); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", nil)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(platform.Title, "title").Message("平台名不能为空!")
valid.MaxSize(platform.Title, 30, "title").Message("平台名不能超过30个字符!")
valid.Required(platform.Alias, "alias").Message("别名不能为空!")
valid.AlphaNumeric(platform.Alias, "alias").Message("别名不能含有中文和特别符号!")
valid.MaxSize(platform.Alias, 30, "alias").Message("别名不能超过30个字符!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
var pt models.Platform
if err := o.Raw("SELECT * FROM platform WHERE id != ? AND alias = ?", platform.ID, platform.Alias).QueryRow(&pt); err == nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "别名已被使用,请换一个试试!", err)
c.ServeJSON()
return
}
// exist ? and create
platform.ID = 0
if isExist, _, err := o.ReadOrCreate(&platform, "Title"); err == nil {
if isExist {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "添加成功!", nil)
} else {
c.Data["json"] = utils.ResponseError(c.Ctx, "平台名已被使用,请换一个试试!", nil)
}
} else {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "服务异常", err)
}
c.ServeJSON()
return
}
// Delete delete remove admin
func (c *PlatformController) Delete() {
o := orm.NewOrm()
id, _ := strconv.ParseInt(c.Ctx.Input.Param(":id"), 10, 64)
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
if _admin.Root != 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "您没有权限删除平台!", nil)
c.ServeJSON()
return
}
// platform
platform := models.Platform{ID: id}
// exist
if err := o.Read(&platform); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "删除失败,平台不存在!", err)
c.ServeJSON()
return
}
if num, err := o.Delete(&platform); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "删除失败!", nil)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "删除成功!", num)
}
c.ServeJSON()
}
// List get admin all
func (c *PlatformController) List() {
o := orm.NewOrm()
var platforms []models.Platform
qs := o.QueryTable(new(models.Platform))
if _, err := qs.OrderBy("id").All(&platforms); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败!", err)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &platforms)
}
c.ServeJSON()
}
package controllers
import (
"encoding/json"
"kefu_server/models"
"kefu_server/utils"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
)
// QiniuController struct
type QiniuController struct {
beego.Controller
}
// Get get qiniu config info
func (c *QiniuController) Get() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
if _admin.Root != 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "您没有权限获取配置!", nil)
c.ServeJSON()
return
}
qiniuSetting := models.QiniuSetting{ID: 1}
if err := o.Read(&qiniuSetting); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败!", err)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &qiniuSetting)
}
c.ServeJSON()
}
// Put update
func (c *QiniuController) Put() {
qiniuSetting := models.QiniuSetting{}
qiniuSetting.UpdateAt = time.Now().Unix()
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &qiniuSetting); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", nil)
c.ServeJSON()
return
}
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
if _admin.Root != 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "您没有权限设置!", nil)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(qiniuSetting.Bucket, "bucket").Message("bucket不能为空!")
valid.MaxSize(qiniuSetting.Bucket, 100, "bucket").Message("bucket不能超过100字符!")
valid.Required(qiniuSetting.AccessKey, "access_key").Message("access_key不能为空!")
valid.MaxSize(qiniuSetting.AccessKey, 100, "access_key").Message("access_key不能超过100字符!")
valid.Required(qiniuSetting.SecretKey, "secret_key").Message("secret_key不能为空!")
valid.MaxSize(qiniuSetting.SecretKey, 100, "secret_key").Message("secret_key不能超过100字符!")
valid.Required(qiniuSetting.Host, "host").Message("host不能为空!")
valid.MaxSize(qiniuSetting.Host, 100, "host").Message("host不能超过100字符!")
if valid.HasErrors() {
for _, err := range valid.Errors {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
qiniuSetting.ID = 1
qiniuSetting.UpdateAt = time.Now().Unix()
if _, err := o.Update(&qiniuSetting); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "更新失败!", err)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "更新成功!", &qiniuSetting)
}
c.ServeJSON()
}
package controllers
import (
"encoding/json"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
"kefu_server/im"
"kefu_server/models"
"kefu_server/utils"
"strconv"
"strings"
"time"
)
// RobotController struct
type RobotController struct {
beego.Controller
}
// Get get robot
func (c *RobotController) Get() {
o := orm.NewOrm()
id, _ := strconv.ParseInt(c.Ctx.Input.Param(":id"), 10, 64)
robot := models.Robot{ID: id}
if err := o.Read(&robot); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "获取失败!", err)
c.ServeJSON()
return
}
robot.Artificial = strings.Trim(robot.Artificial, "|")
robot.KeyWord = strings.Trim(robot.KeyWord, "|")
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &robot)
c.ServeJSON()
}
// Delete delete robot
func (c *RobotController) Delete() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
if _admin.Root != 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "您没有权限删除机器人!", nil)
c.ServeJSON()
return
}
id, _ := strconv.ParseInt(c.Ctx.Input.Param(":id"), 10, 64)
robot := models.Robot{ID: id}
// exist
if err := o.Read(&robot); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "删除失败,机器人不存在!", err)
c.ServeJSON()
return
}
if robot.System == 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "不能删除该机器人,系统保留!", nil)
c.ServeJSON()
return
}
if num, err := o.Delete(&robot); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "删除失败!", nil)
} else {
// init robots
im.RobotInit()
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "删除成功!", &num)
}
c.ServeJSON()
}
// Post add robot
func (c *RobotController) Post() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
if _admin.Root != 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "您没有权限添加机器人!", nil)
c.ServeJSON()
return
}
// request body
var robot models.Robot
robot.CreateAt = time.Now().Unix()
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &robot); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", err)
c.ServeJSON()
return
}
// exist robot?
oldRobot := models.Robot{NickName: robot.NickName}
if err := o.Read(&oldRobot, "NickName"); err == nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "已存在一个名为"+robot.NickName+"的机器人!", nil)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(robot.Avatar, "avatar").Message("请设置一个机器人头像!")
valid.Required(robot.NickName, "nickname").Message("机器人名不能为空!")
valid.Required(robot.Welcome, "welcome").Message("请设置机器人欢迎语!")
valid.Required(robot.Understand, "understand").Message("请设置机器人无法识别回复语!")
valid.Required(robot.KeyWord, "keyword").Message("请设置检索知识库热词!")
valid.Required(robot.Artificial, "artificial").Message("请设置转人工关键字!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
// insert
robot.Artificial = strings.Trim(robot.Artificial, "|")
if robot.Artificial != "" {
robot.Artificial = "|" + robot.Artificial + "|"
}
robot.KeyWord = strings.Trim(robot.KeyWord, "|")
if robot.KeyWord != "" {
robot.KeyWord = "|" + robot.KeyWord + "|"
}
if id, err := o.Insert(&robot); err == nil {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "添加成功!", &id)
im.RobotInit()
} else {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "服务异常!", err)
}
c.ServeJSON()
}
// Put update robot
func (c *RobotController) Put() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
if _admin.Root != 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "您没有权限修改机器人!", nil)
c.ServeJSON()
return
}
// RequestBody
var robot models.Robot
robot.UpdateAt = time.Now().Unix()
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &robot); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", err)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(robot.ID, "id").Message("机器人不存在!")
valid.Required(robot.Avatar, "avatar").Message("请设置一个机器人头像!")
valid.Required(robot.NickName, "nickname").Message("机器人名不能为空!")
valid.Required(robot.Welcome, "welcome").Message("请设置机器人欢迎语!")
valid.Required(robot.Understand, "understand").Message("请设置机器人无法识别回复语!")
valid.Required(robot.KeyWord, "keyword").Message("请设置检索知识库热词!")
valid.Required(robot.Artificial, "artificial").Message("请设置转人工关键字!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
// is change default robot ?
if robot.System == 1 && robot.Switch == 0 {
c.Data["json"] = utils.ResponseError(c.Ctx, "不能暂停该机器人!", nil)
c.ServeJSON()
return
}
// is change default robot ?
if robot.System == 1 && robot.Platform != 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "不能修改该机器人平台!", nil)
c.ServeJSON()
return
}
// exist
oldRobot := models.Robot{ID: robot.ID}
if err := o.Read(&oldRobot); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "机器人"+robot.NickName+"不存在", nil)
c.ServeJSON()
return
}
// robot name exist
oldRobot = models.Robot{NickName: robot.NickName}
if err := o.Read(&oldRobot, "NickName"); err == nil && oldRobot.ID != robot.ID {
c.Data["json"] = utils.ResponseError(c.Ctx, "已存在一个名为"+robot.NickName+"的机器人", nil)
c.ServeJSON()
return
}
// insert
robot.Artificial = strings.Trim(robot.Artificial, "|")
if robot.Artificial != "" {
robot.Artificial = "|" + robot.Artificial + "|"
}
robot.KeyWord = strings.Trim(robot.KeyWord, "|")
if robot.KeyWord != "" {
robot.KeyWord = "|" + robot.KeyWord + "|"
}
if _, err := o.Update(&robot, "NickName", "Avatar", "Welcome", "Understand", "Artificial", "Switch", "UpdateAt", "KeyWord", "TimeoutText", "NoServices", "LoogTimeWaitText"); err == nil {
robot.Artificial = strings.Trim(robot.Artificial, "|")
robot.KeyWord = strings.Trim(robot.KeyWord, "|")
robot.CreateAt = oldRobot.CreateAt
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "修改成功!", &robot)
im.RobotInit()
} else {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "修改失败!", err)
}
c.ServeJSON()
}
// List get robot all
func (c *RobotController) List() {
o := orm.NewOrm()
robot := new(models.Robot)
qs := o.QueryTable(robot)
// query
var lists []models.Robot
if _, err := qs.OrderBy("create_at").All(&lists); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败!", err)
c.ServeJSON()
return
}
for index := range lists {
lists[index].Artificial = strings.Trim(lists[index].Artificial, "|")
lists[index].KeyWord = strings.Trim(lists[index].KeyWord, "|")
}
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &lists)
c.ServeJSON()
}
package controllers
import (
"encoding/json"
"kefu_server/utils"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
)
// ServicesStatisticalController struct
type ServicesStatisticalController struct {
beego.Controller
}
// ServicesStatisticalPaginationData struct
type ServicesStatisticalPaginationData struct {
PageSize int `json:"page_size"`
PageOn int `json:"page_on"`
Cid int64 `json:"cid"`
Date string `json:"date"`
IsDeWeighting bool `json:"is_de_weighting"`
Total int64 `json:"total"`
List interface{} `json:"list"`
}
// List Services Statistical
func (c *ServicesStatisticalController) List() {
// request body
var paginationData ServicesStatisticalPaginationData
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &paginationData); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", err)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(paginationData.PageOn, "page_on").Message("page_on不能为空!")
valid.Required(paginationData.PageSize, "page_size").Message("page_size不能为空!")
valid.Required(paginationData.Cid, "cid").Message("cid不能为空!")
valid.Required(paginationData.Date, "date").Message("date不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
o := orm.NewOrm()
layoutDate := "2006-01-02 15:04:05"
loc, _ := time.LoadLocation("Local")
startDateStr := paginationData.Date + " 00:00:00"
endDateStr := paginationData.Date + " 23:59:59"
startDate, _ := time.ParseInLocation(layoutDate, startDateStr, loc)
endDate, _ := time.ParseInLocation(layoutDate, endDateStr, loc)
var params []orm.Params
type TotalModel struct {
Count int64
}
var totalModel TotalModel
// Deduplication
addSQL := " COUNT(*) "
if paginationData.IsDeWeighting {
addSQL = " count(distinct user_account) "
}
o.Raw("SELECT "+addSQL+" AS `count` FROM services_statistical AS s INNER JOIN (SELECT * FROM `user`) AS u ON s.user_account = u.id AND s.service_account = ? AND s.create_at > ? AND s.create_at < ?", paginationData.Cid, startDate.Unix(), endDate.Unix()).QueryRow(&totalModel)
paginationData.Total = totalModel.Count
// Deduplication
addSQL1 := " "
if paginationData.IsDeWeighting {
addSQL1 = " GROUP BY `user_account` "
}
if counter, _ := o.Raw("SELECT s.id, s.user_account, s.service_account,s.create_at, s.transfer_account,s.platform,u.nickname FROM services_statistical AS s INNER JOIN (SELECT * FROM `user` ) AS u ON s.user_account = u.id AND s.service_account = ? AND s.create_at > ? AND s.create_at < ? "+addSQL1+" ORDER BY s.create_at DESC LIMIT ?,?", paginationData.Cid, startDate.Unix(), endDate.Unix(), (paginationData.PageOn-1)*paginationData.PageSize, paginationData.PageSize).Values(&params); counter <= 0 {
paginationData.List = []string{}
c.Data["json"] = paginationData
} else {
paginationData.List = params
c.Data["json"] = paginationData
}
c.ServeJSON()
}
package controllers
import (
"encoding/base64"
"encoding/json"
"kefu_server/models"
"kefu_server/utils"
"strconv"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
)
// ShortcutController struct
type ShortcutController struct {
beego.Controller
}
// Get get shortcut
func (c *ShortcutController) Get() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
id, _ := strconv.ParseInt(c.Ctx.Input.Param(":id"), 10, 64)
shortcut := models.Shortcut{ID: id}
if err := o.Read(&shortcut); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败,内容不存在!", err)
} else {
if _admin.ID != shortcut.UID {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败,内容不存在!", nil)
} else {
title, _ := base64.StdEncoding.DecodeString(shortcut.Title)
content, _ := base64.StdEncoding.DecodeString(shortcut.Content)
shortcut.Title = string(title)
shortcut.Content = string(content)
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &shortcut)
}
}
c.ServeJSON()
}
// Put update shortcut
func (c *ShortcutController) Put() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
// request body
shortcut := models.Shortcut{}
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &shortcut); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", nil)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(shortcut.Title, "title").Message("标题不能为空!")
valid.Required(shortcut.Content, "content").Message("内容不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
oldShortcut := models.Shortcut{ID: shortcut.ID}
_ = o.Read(&oldShortcut)
if oldShortcut.UID != _admin.ID {
c.Data["json"] = utils.ResponseError(c.Ctx, "更新失败,内容不存在!", nil)
c.ServeJSON()
return
}
// update
shortcut.Title = base64.StdEncoding.EncodeToString([]byte(shortcut.Title))
shortcut.Content = base64.StdEncoding.EncodeToString([]byte(shortcut.Content))
if _, err := o.Update(&shortcut, "UpdateAt", "Content", "Title"); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "更新失败!", err)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "更新成功!", nil)
}
c.ServeJSON()
}
// Post add new shortcut
func (c *ShortcutController) Post() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
// request body
var shortcut models.Shortcut
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &shortcut); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", nil)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(shortcut.Title, "title").Message("标题不能为空!")
valid.Required(shortcut.Content, "content").Message("内容不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
// exist ? create
shortcut.UID = _admin.ID
shortcut.CreateAt = time.Now().Unix()
shortcut.Title = base64.StdEncoding.EncodeToString([]byte(shortcut.Title))
shortcut.Content = base64.StdEncoding.EncodeToString([]byte(shortcut.Content))
if isExist, createID, err := o.ReadOrCreate(&shortcut, "Title", "Uid"); err == nil {
if isExist {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "添加成功!", &createID)
} else {
c.Data["json"] = utils.ResponseError(c.Ctx, "已存在相同的内容!", nil)
}
} else {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "服务异常!", err)
}
c.ServeJSON()
return
}
// Delete delete remove shortcut
func (c *ShortcutController) Delete() {
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
id, _ := strconv.ParseInt(c.Ctx.Input.Param(":id"), 10, 64)
shortcut := models.Shortcut{ID: id}
// exist
if err := o.Read(&shortcut); err != nil || shortcut.UID != _admin.ID {
c.Data["json"] = utils.ResponseError(c.Ctx, "删除失败,内容不存在!", nil)
c.ServeJSON()
return
}
if num, err := o.Delete(&shortcut); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "删除失败!", nil)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "删除成功!", &num)
}
c.ServeJSON()
}
// List get shortcut all
func (c *ShortcutController) List() {
o := orm.NewOrm()
shortcut := new(models.Shortcut)
qs := o.QueryTable(shortcut)
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
// query
var lists []models.Shortcut
if _, err := qs.Filter("uid", _admin.ID).OrderBy("-create_at").All(&lists); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败!", err)
c.ServeJSON()
return
}
// base 64转换回来
for index, shortcut := range lists {
title, _ := base64.StdEncoding.DecodeString(shortcut.Title)
content, _ := base64.StdEncoding.DecodeString(shortcut.Content)
lists[index].Title = string(title)
lists[index].Content = string(content)
}
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &lists)
c.ServeJSON()
}
package controllers
import (
"encoding/json"
"kefu_server/models"
"kefu_server/utils"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
)
// SystemController struct
type SystemController struct {
beego.Controller
}
// Get get info
func (c *SystemController) Get() {
o := orm.NewOrm()
system := models.System{ID: 1}
if err := o.Read(&system); err != nil {
logs.Error(err)
c.Data["json"] = &models.Response{Code: 400, Message: "查询失败", Data: err}
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &system)
}
c.ServeJSON()
}
// Put update system
func (c *SystemController) Put() {
system := models.System{}
system.UpdateAt = time.Now().Unix()
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &system); err != nil {
logs.Error(err)
c.Data["json"] = &models.Response{Code: 400, Message: "参数错误", Data: nil}
c.ServeJSON()
return
}
o := orm.NewOrm()
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
if _admin.Root != 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "您没有权限修改系统设置!", nil)
c.ServeJSON()
return
}
// validation upload mode
var uploadValues []orm.Params
_, _ = o.Raw("SELECT * FROM uploads_config where id = ?", system.UploadMode).Values(&uploadValues)
if len(uploadValues) <= 0 {
c.Data["json"] = utils.ResponseError(c.Ctx, "上传模型选项不存在!", nil)
c.ServeJSON()
return
}
// validation request
valid := validation.Validation{}
valid.Required(system.Title, "title").Message("系统名称不能为空!")
valid.MaxSize(system.Title, 50, "title").Message("系统名称不能超过50个字符!")
valid.MaxSize(system.CopyRight, 80, "copy_right").Message("版权信息不能超过80个字符!")
valid.Required(system.Logo, "logo").Message("系统LOGO不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
system.ID = 1
system.UpdateAt = time.Now().Unix()
if _, err := o.Update(&system); err != nil {
logs.Error(err)
c.Data["json"] = utils.ResponseError(c.Ctx, "更新失败", err)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "更新成功!", &system)
}
c.ServeJSON()
}
package controllers
import (
"kefu_server/models"
"kefu_server/utils"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
)
// UploadsConfigController struct
type UploadsConfigController struct {
beego.Controller
}
// Config get upload config
func (c *UploadsConfigController) Config() {
o := orm.NewOrm()
var configs []models.UploadsConfig
if _, err := o.QueryTable(new(models.UploadsConfig)).All(&configs); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败", err)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &configs)
}
c.ServeJSON()
}
package controllers
import (
"encoding/json"
"kefu_server/models"
"kefu_server/utils"
"strconv"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/validation"
)
// UserController struct
type UserController struct {
beego.Controller
}
// Get get a user
func (c *UserController) Get() {
o := orm.NewOrm()
id, _ := strconv.ParseInt(c.Ctx.Input.Param(":id"), 10, 64)
user := models.User{ID: id}
if err := o.Read(&user); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败,用户不存在!", err)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &user)
}
c.ServeJSON()
}
// Put update a user
func (c *UserController) Put() {
// request
user := models.User{}
user.UpdateAt = time.Now().Unix()
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &user); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误!", err)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(user.ID, "id").Message("用户ID不能为空!")
valid.MaxSize(user.Address, 80, "address").Message("用户所在地区不能超过80个字符!")
valid.MaxSize(user.NickName, 15, "nickname").Message("用户昵称不能超过15个字符!")
valid.MaxSize(user.Phone, 20, "phone").Message("用户联系方式不能超过20个字符!")
valid.MaxSize(user.Remarks, 150, "remarks").Message("用户备注不能超过150个字符!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
// orm
o := orm.NewOrm()
if _, err := o.Update(&user, "Address", "NickName", "Phone", "Remarks", "UpdateAt", "UpdateAt", "Avatar"); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "更新失败!", nil)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "更新成功!", &user)
}
c.ServeJSON()
}
// Post add new user
// 用户创建 请移步(此处暂不提供创建用户逻辑) /v1/im/register
func (c *UserController) Post() {
c.ServeJSON()
}
// Delete delete remove user
func (c *UserController) Delete() {
// orm instance
o := orm.NewOrm()
id, _ := strconv.ParseInt(c.Ctx.Input.Param(":id"), 10, 64)
// is admin ?
token := c.Ctx.Input.Header("Authorization")
_admin := models.Admin{Token: token}
_ = o.Read(&_admin, "Token")
if _admin.Root != 1 {
c.Data["json"] = utils.ResponseError(c.Ctx, "您没有权限删除用户!", nil)
c.ServeJSON()
return
}
// user
user := models.User{ID: id}
// exist
if err := o.Read(&user); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "删除失败,用户不存在!", err)
c.ServeJSON()
return
}
if num, err := o.Delete(&user); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "删除失败!", nil)
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "删除成功!", &num)
}
c.ServeJSON()
}
// UsersPaginationData struct
type UsersPaginationData struct {
PageSize int `json:"page_size"`
PageOn int `json:"page_on"`
Keyword string `json:"keyword"`
Total int64 `json:"total"`
Platform int64 `json:"platform"`
DateStart string `json:"date_start"`
DateEnd string `json:"date_end"`
List interface{} `json:"list"`
}
// Users get users
func (c *UserController) Users() {
// request body
var usersPaginationData UsersPaginationData
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &usersPaginationData); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "参数错误", err)
c.ServeJSON()
return
}
// validation
valid := validation.Validation{}
valid.Required(usersPaginationData.PageOn, "page_on").Message("page_on不能为空!")
valid.Required(usersPaginationData.PageSize, "page_size").Message("page_size不能为空!")
if valid.HasErrors() {
for _, err := range valid.Errors {
c.Data["json"] = utils.ResponseError(c.Ctx, err.Message, nil)
break
}
c.ServeJSON()
return
}
// orm instance
o := orm.NewOrm()
qs := o.QueryTable(new(models.User))
cond := orm.NewCondition()
var cond1 *orm.Condition
var cond2 *orm.Condition
if usersPaginationData.Keyword != "" {
cond1 = cond.Or("nickname__icontains", usersPaginationData.Keyword).Or("phone__icontains", usersPaginationData.Keyword).Or("remarks__icontains", usersPaginationData.Keyword)
}
// exist platfrom id?
if usersPaginationData.Platform != 0 && usersPaginationData.Platform != 1 {
cond2 = cond.And("platform", usersPaginationData.Platform)
}
// exist platfrom date?
if usersPaginationData.DateStart != "" && usersPaginationData.DateEnd != "" {
layoutDate := "2006-01-02 15:04:05"
loc, _ := time.LoadLocation("Local")
dateStartString := usersPaginationData.DateStart + " 00:00:00"
dateEndString := usersPaginationData.DateEnd + " 23:59:59"
dateStart, _ := time.ParseInLocation(layoutDate, dateStartString, loc)
dateEnd, _ := time.ParseInLocation(layoutDate, dateEndString, loc)
cond2 = cond2.And("create_at__gte", dateStart.Unix()).And("create_at__lte", dateEnd.Unix())
}
// query
var lists []models.User
cond3 := cond.AndCond(cond2).OrCond(cond1)
qs = qs.SetCond(cond3)
qs = qs.OrderBy("-online", "-create_at").Limit(usersPaginationData.PageSize)
if _, err := qs.Offset((usersPaginationData.PageOn - 1) * usersPaginationData.PageSize).All(&lists); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败", err)
c.ServeJSON()
return
}
total, _ := qs.Count()
usersPaginationData.Total = total
usersPaginationData.List = &lists
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", &usersPaginationData)
c.ServeJSON()
}
// OnLineCount get all online user count
func (c *UserController) OnLineCount() {
o := orm.NewOrm()
if onLineCount, err := o.QueryTable(models.User{}).Filter("online", 1).Count(); err != nil {
c.Data["json"] = utils.ResponseError(c.Ctx, "查询失败!", nil)
c.ServeJSON()
} else {
c.Data["json"] = utils.ResponseSuccess(c.Ctx, "查询成功!", onLineCount)
c.ServeJSON()
}
}
package filters
import (
"kefu_server/models"
"kefu_server/utils"
"math/big"
"regexp"
"strconv"
"strings"
"time"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/orm"
)
// 判断用户是否带有token
func err401(ctx *context.Context) {
ctx.Output.Header("Content-Type", "application/json")
ctx.ResponseWriter.WriteHeader(401)
_ = ctx.Output.Body([]byte("{\"code\": \"401\", \"message\": \"未登录,或登录已失效\", \"data\": \"token expired\"}"))
}
// FilterToken token
var FilterToken = func(ctx *context.Context) {
// 地址白名单
whitelist := []string{
"/v1/auth/login",
"/v1/auth/logout",
}
oldToken := ctx.Input.Header("Authorization")
isExistInSlice := utils.InExistInSlice(ctx.Request.RequestURI, whitelist)
isMatch, _ := regexp.MatchString(`^Bearer\s`, oldToken)
if (isExistInSlice == false && oldToken == "") || !isMatch {
err401(ctx)
return
}
if isExistInSlice == false && oldToken != "" {
token := strings.Split(oldToken, " ")[1]
admin := models.Admin{Token: oldToken}
o := orm.NewOrm()
if err := o.Read(&admin, "Token"); err != nil {
err401(ctx)
return
}
if err := utils.ValidateToken(token); err != nil {
err401(ctx)
return
}
}
// 判断是否需要刷新token
token := strings.Split(oldToken, " ")[1]
parmMap, err := utils.DecodeToken(token)
if err != nil {
err401(ctx)
return
}
newNum := big.NewRat(1, 1)
newNum.SetFloat64(parmMap["exp"].(float64))
exp, _ := strconv.ParseInt(newNum.FloatString(0), 10, 64)
// 该换token了
if time.Now().Unix()+60*60*2 >= exp {
if newToken, err := utils.RefreshToken(token); err == nil {
ctx.Output.Header("Authorization", newToken)
}
}
ctx.Output.Header("Authorization", oldToken)
}
# ! /bin/sh
SERVICE_PATH="/home/kefu_server"
SERVICE_NAME="kefu_server"
START_CMD="nohup ./$SERVICE_NAME &"
LOG_FILE="restart.log"
cd $SERVICE_PATH
pwd
while true
do
procnum=`ps -ef|grep $SERVICE_NAME|grep -v grep|wc -l`
if [ $procnum -eq 0 ]
then
echo "start service...................."
echo `date +%Y-%m-%d` `date +%H:%M:%S` $SERVICE_NAME >>$LOG_FILE
${START_CMD}
fi
sleep 5
done
package im
import (
"encoding/base64"
"encoding/json"
"kefu_server/models"
"strconv"
"time"
"github.com/Xiaomi-mimc/mimc-go-sdk"
"github.com/astaxie/beego/orm"
)
// PushNewContacts 推送最新聊天列表给客服
func PushNewContacts(accountID int64, robot *mimc.MCUser) {
o := orm.NewOrm()
var contactData []models.ContactData
// 消息体
message := models.Message{}
message.BizType = "contacts"
robotAccount, _ := strconv.ParseInt(robot.AppAccount(), 10, 64)
message.FromAccount = robotAccount
message.Timestamp = time.Now().Unix()
rCount, _ := o.Raw("SELECT c.id AS cid,c.to_account,c.is_session_end, c.last_message,c.last_message_type,c.from_account, c.create_at AS contact_create_at,u.*, IFNULL(m.`count`,0) AS `read` FROM `contact` c LEFT JOIN `user` u ON c.from_account = u.id LEFT JOIN (SELECT to_account,from_account, COUNT(*) as `count` FROM message WHERE `read` = 1 GROUP BY to_account,from_account) m ON m.to_account = c.to_account AND m.from_account = c.from_account WHERE c.to_account = ? AND c.delete = 0 ORDER BY c.create_at DESC", accountID).QueryRows(&contactData)
if rCount == 0 {
contactData = []models.ContactData{}
}
// base 64转换回来
for index, contact := range contactData {
payload, _ := base64.StdEncoding.DecodeString(contact.LastMessage)
contactData[index].LastMessage = string(payload)
}
message.ToAccount = accountID
messageContentByte, _ := json.Marshal(contactData)
message.Payload = string(messageContentByte)
messageJSON, _ := json.Marshal(message)
messageString := base64.StdEncoding.EncodeToString([]byte(messageJSON))
robot.SendMessage(strconv.FormatInt(accountID, 10), []byte(messageString))
}
package im
import (
"container/list"
"encoding/base64"
"encoding/json"
"kefu_server/models"
msg "github.com/Xiaomi-mimc/mimc-go-sdk/message"
)
// MsgHandler ...
type MsgHandler struct {
appAccount string
}
// NewMsgHandler ...
func NewMsgHandler(appAccount string) *MsgHandler {
return &MsgHandler{appAccount}
}
// HandleMessage ...
func (c MsgHandler) HandleMessage(packets *list.List) {
for ele := packets.Front(); ele != nil; ele = ele.Next() {
// 收到的原始消息
p2pMsg := ele.Value.(*msg.P2PMessage)
// 取出用户发的消息内容
var message models.Message
msgContent, _ := base64.StdEncoding.DecodeString(string(p2pMsg.Payload()))
//logs.Info("收到消息", *p2pMsg.FromAccount(), *p2pMsg.ToAccount(), *p2pMsg.Timestamp(),*p2pMsg.PacketId(),*p2pMsg.Sequence())
_ = json.Unmarshal(msgContent, &message)
MessageP2P(message)
}
}
// HandleGroupMessage 下面可以自己去实现一些东西(顾名思义MIMC接口)
func (c MsgHandler) HandleGroupMessage(packets *list.List) {
//for ele := packets.Front(); ele != nil; ele = ele.Next() {
// p2tmsg := ele.Value.(*msg.P2TMessage)
// logger.Info("[%v] [handle p2t msg]%v -> %v: %v, pcktId: %v, timestamp: %v.", c.appAccount, *(p2tmsg.FromAccount()), *(p2tmsg.GroupId()), string(p2tmsg.Payload()), *(p2tmsg.PacketId()), *(p2tmsg.Timestamp()))
//}
}
// HandleServerAck ...
func (c MsgHandler) HandleServerAck(packetID *string, sequence, timestamp *int64, errMsg *string) {
//logs.Info("[%v] [handle server ack] packetId:%v, seqId: %v, timestamp:%v.", c.appAccount, *packetId, *sequence, *timestamp)
}
// HandleSendMessageTimeout ...
func (c MsgHandler) HandleSendMessageTimeout(message *msg.P2PMessage) {
//logs.Info("[%v] [handle p2pmsg timeout] packetId:%v, msg:%v, time: %v.", c.appAccount, *(message.PacketId()), string(message.Payload()), time.Now())
}
// HandleSendGroupMessageTimeout ...
func (c MsgHandler) HandleSendGroupMessageTimeout(message *msg.P2TMessage) {
// logger.Info("[%v] [handle p2tmsg timeout] packetId:%v, msg:%v.", c.appAccount, *(message.PacketId()), string(message.Payload()))
}
package im
import "github.com/astaxie/beego/logs"
// StatusHandler struct
type StatusHandler struct {
appAccount string
}
// NewStatusHandler newStatusHandler
func NewStatusHandler(appAccount string) *StatusHandler {
return &StatusHandler{appAccount}
}
// HandleChange handleChange
func (c StatusHandler) HandleChange(isOnline bool, errType, errReason, errDescription *string) {
if isOnline {
logs.Info("机器人霸道上线 status changed: online.", "")
} else {
// 有机器人掉线,重新登录
logs.Error("[机器人挂掉了] status changed: offline,errType:%v, errReason:%v, errDes:%v", *errType, *errReason, *errDescription)
}
}
package im
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"strings"
"github.com/astaxie/beego"
)
// NewTokenHandler ...
func NewTokenHandler(appAccount string) *TokenHandler {
tokenHandler := new(TokenHandler)
tokenHandler.httpURL = beego.AppConfig.String("mimc_HttpUrl")
tokenHandler.AppID, _ = beego.AppConfig.Int64("mimc_appId")
tokenHandler.AppKey = beego.AppConfig.String("mimc_appKey")
tokenHandler.AppSecret = beego.AppConfig.String("mimc_appSecret")
tokenHandler.AppAccount = appAccount
return tokenHandler
}
// FetchToken ...
func (c *TokenHandler) FetchToken() *string {
jsonBytes, err := json.Marshal(*c)
if err != nil {
return nil
}
requestJSONBodygo := bytes.NewBuffer(jsonBytes).String()
request, err := http.Post(c.httpURL, "application/json", strings.NewReader(requestJSONBodygo))
if err != nil {
return nil
}
defer request.Body.Close()
body, err := ioutil.ReadAll(request.Body)
if err != nil {
return nil
}
token := string(body)
return &token
}
package im
import (
"encoding/base64"
"kefu_server/models"
"strconv"
"time"
"github.com/astaxie/beego/orm"
)
// MessageInto push message
func MessageInto(message models.Message, isKF bool) {
// orm instance
o := orm.NewOrm()
// 不处理的类型
if message.BizType == "contacts" || message.BizType == "pong" || message.BizType == "welcome" || message.BizType == "into" || message.BizType == "search_knowledge" {
return
}
// 判断是否是撤回消息(删掉数据库消息)
if message.BizType == "cancel" {
key, _ := strconv.ParseInt(message.Payload, 10, 64)
_, _ = o.Raw("DELETE FROM message WHERE from_account = ? AND to_account = ? AND `key` = ?", message.FromAccount, message.ToAccount, key).Exec()
}
// message create time
message.Timestamp = time.Now().Unix()
// content内容转base64
message.Payload = base64.StdEncoding.EncodeToString([]byte(message.Payload))
// 过滤掉下面类型的消息不入库
if !(message.BizType == "handshake") {
if !isKF {
// 默认已读消息
message.Read = 0
user := models.User{ID: message.ToAccount}
if err := o.Read(&user); err == nil && user.Online == 0 {
message.Read = 1
}
if user.IsWindow == 0 {
message.Read = 1
}
}
// message.BizType == "end" is not read
if message.BizType == "end" || message.BizType == "timeout" {
message.Read = 0
}
// 消息入库
_, _ = o.Insert(&message)
}
// 判断是否和机器人对话(不处理聊天列表)
var r []orm.Params
robotCount, _ := o.Raw("SELECT * FROM robot WHERE id IN(?, ?)", message.ToAccount, message.FromAccount).Values(&r)
if robotCount > 0 {
return
}
// 处理客服聊天列表
var contact models.Contact
qs := o.QueryTable(new(models.Contact))
if err := qs.Filter("from_account__in", message.FromAccount, message.ToAccount).Filter("to_account__in", message.ToAccount, message.FromAccount).One(&contact); err != nil {
contact.ToAccount = message.ToAccount
contact.FromAccount = message.FromAccount
contact.LastMessageType = message.BizType
contact.CreateAt = time.Now().Unix()
contact.LastMessage = message.Payload
o.Insert(&contact)
} else {
contact.LastMessageType = message.BizType
contact.CreateAt = time.Now().Unix()
contact.LastMessage = message.Payload
contact.IsSessionEnd = 0
contact.Delete = 0
if message.BizType == "end" || message.BizType == "timeout" {
contact.IsSessionEnd = 1
}
o.Update(&contact)
}
}
package im
import (
"encoding/base64"
"encoding/json"
"kefu_server/models"
"strconv"
"strings"
"time"
"github.com/Xiaomi-mimc/mimc-go-sdk"
"github.com/astaxie/beego/cache"
"github.com/astaxie/beego/orm"
)
// PayloadData struct
type PayloadData struct {
Payload string `json:"payload"`
}
// KnowledgeBaseData struct
type KnowledgeBaseData struct {
Title string `json:"title"`
}
// ContactAdminCount struct
type ContactAdminCount struct {
Count string `json:"count"`
ToAccount string `json:"to_account"`
}
// adminData struct
type adminData struct {
ID int64 `json:"id"`
NickName string `json:"nickname"`
Avatar string `json:"avatar"`
}
// BmCache ...
var BmCache, _ = cache.NewCache("memory", `{"interval":60}`)
// MessageP2P p2p message
func MessageP2P(message models.Message) {
// orm
o := orm.NewOrm()
// 当前服务机器人
var robot *mimc.MCUser
var robotID int64
msgToAccount := strconv.FormatInt(message.ToAccount, 10)
isFromAccountRobot := false
for _, robot = range Robots {
robotID, _ = strconv.ParseInt(robot.AppAccount(), 10, 64)
if robotID == message.FromAccount {
isFromAccountRobot = true
return
}
if toAccount := robot.AppAccount(); toAccount == msgToAccount {
break
}
}
if isFromAccountRobot {
return
}
// 判断机器人是否在线
if robot.Status() == 1 {
robot.Login()
}
// 是否是入库消息(中转入库)
if message.BizType == "into" {
var intoMessage models.Message
intoMessageString, _ := base64.StdEncoding.DecodeString(message.Payload)
_ = json.Unmarshal(intoMessageString, &intoMessage)
// 接收方是否是客服
isKF := false
admin := models.Admin{ID: intoMessage.ToAccount}
if err := o.Read(&admin); err == nil {
isKF = true
intoMessage.Read = 0
if admin.Online == 0 || admin.CurrentConUser != intoMessage.FromAccount {
intoMessage.Read = 1
}
}
MessageInto(intoMessage, isKF)
if isKF {
PushNewContacts(intoMessage.ToAccount, robot)
}
return
}
// 不处理的消息类型
if message.BizType == "cancel" {
return
}
// 取出发信人信息
userData := models.User{ID: message.FromAccount}
userInfoKey := "userInfo"
if userTemp := BmCache.Get(userInfoKey); userTemp == nil {
userData = models.User{ID: message.FromAccount}
_ = o.Read(&userData)
robotDataJSON, _ := json.Marshal(userData)
_ = BmCache.Put(userInfoKey, robotDataJSON, 60*time.Second)
} else {
_ = json.Unmarshal([]byte(string(userTemp.([]byte))), &userData)
}
// 获取公司配置
companyData := models.Company{ID: 1}
systemInfoKey := "systemInfo"
if systemTemp := BmCache.Get(systemInfoKey); systemTemp == nil {
companyData = models.Company{ID: 1}
_ = o.Read(&companyData)
robotDataJSON, _ := json.Marshal(companyData)
_ = BmCache.Put(systemInfoKey, robotDataJSON, 60*time.Second)
} else {
_ = json.Unmarshal([]byte(string(systemTemp.([]byte))), &companyData)
}
// 数据库获取机器人配置信息
robotData := models.Robot{ID: message.ToAccount}
cacheRobotKey := "robot_" + msgToAccount
if robotDataTemp := BmCache.Get(cacheRobotKey); robotDataTemp == nil {
robotData = models.Robot{ID: message.ToAccount}
_ = o.Read(&robotData)
robotDataJSON, _ := json.Marshal(robotData)
_ = BmCache.Put(cacheRobotKey, robotDataJSON, 60*time.Second)
} else {
_ = json.Unmarshal([]byte(string(robotDataTemp.([]byte))), &robotData)
}
// 消息体
callbackMessage := models.Message{}
// 没有找到子标题的内容, 不明白语句
var understand = robotData.Understand
// 无法匹配知识库默认关键词[]string
roobtKeyWords := strings.Split(strings.Trim(robotData.KeyWord, "|"), "|")
// 返回给对方的消息内容
var messageContent string
// 返回的消息类型
bizType := "text"
//检索关键词知识库消息
var knowledgeBases []KnowledgeBaseData
if message.BizType == "search_knowledge" {
// 默认关键词
var subTitle = ""
for _, value := range roobtKeyWords {
if strings.Contains(message.Payload, value) {
subTitle = subTitle + " sub_title LIKE '%" + value + "%' OR "
}
}
if subTitle != "" {
subTitle = subTitle[1 : len(subTitle)-3]
}
_, _ = o.Raw("SELECT title,sub_title FROM knowledge_base WHERE ("+subTitle+") AND platform IN (?,?) ORDER by rand() limit 5", 1, userData.Platform).QueryRows(&knowledgeBases)
bizType = "search_knowledge"
if len(knowledgeBases) > 0 {
messageContentByte, _ := json.Marshal(knowledgeBases)
messageContent = string(messageContentByte)
} else {
messageContent = ""
}
// 判断是否是握手消息
} else if message.BizType == "handshake" {
messageContent = robotData.Welcome
bizType = "welcome"
} else {
// 判断是否符合转人工
artificial := strings.Split(strings.Trim(robotData.Artificial, "|"), "|")
isTransfer := false
if message.Payload == "人工" {
isTransfer = true
} else {
for i := 0; i < len(artificial); i++ {
if artificial[i] == message.Payload {
isTransfer = true
break
}
}
}
// 符合
if isTransfer {
var admins []models.Admin
_, _ = o.Raw("SELECT a.*, IFNULL(c.count,0) AS `count` FROM admin as a LEFT JOIN (SELECT to_account,COUNT(*) AS count FROM `contact` WHERE is_session_end = 0 GROUP BY to_account) c ON a.id = c.to_account WHERE a.`online` = 1 ORDER BY c.count").QueryRows(&admins)
if len(admins) <= 0 {
messageContent = robotData.NoServices
} else {
bizType = "transfer"
// 平均分配客服
admin := admins[0]
callbackMessage.TransferAccount = admin.ID
adminDataJSON, _ := json.Marshal(adminData{ID: admin.ID, NickName: admin.NickName, Avatar: admin.Avatar})
messageContent = string(adminDataJSON)
// 设置为已删除消息,避免客服端显示两条消息
callbackMessage.Delete = 1
// 发送一条消息告诉客服端
newMsg := models.Message{}
newMsg.BizType = "transfer"
newMsg.FromAccount = message.FromAccount
newMsg.ToAccount = admin.ID
newMsg.Timestamp = time.Now().Unix()
newMsg.TransferAccount = admin.ID
newMsg.Payload = "系统将客户分配给您"
newMsgJSON, _ := json.Marshal(newMsg)
newMsgBase64 := base64.StdEncoding.EncodeToString([]byte(newMsgJSON))
// 消息入库
MessageInto(newMsg, true)
PushNewContacts(admin.ID, robot)
// 发给用户
robot.SendMessage(strconv.FormatInt(admin.ID, 10), []byte(newMsgBase64))
// 转接入库用于统计服务次数
servicesStatistical := models.ServicesStatistical{UserAccount: message.FromAccount, ServiceAccount: admin.ID, Platform: message.Platform, TransferAccount: robotID, CreateAt: time.Now().Unix()}
_, _ = o.Insert(&servicesStatistical)
}
// 不符合去查知识库
} else {
// 数据库查找知识库主标题
qs := o.QueryTable(new(models.KnowledgeBase))
// 先完全匹配
var knowledge models.KnowledgeBase
err := qs.Filter("title", message.Payload).Filter("platform__in", 1, userData.Platform).One(&knowledge)
if err == nil {
bizType = "text"
messageContent = knowledge.Content
// 模糊匹配列表返回列表
} else {
_, _ = o.Raw("SELECT title FROM knowledge_base WHERE title LIKE ? AND platform IN (?,?) ORDER by rand() limit 4", "%"+message.Payload+"%", 1, userData.Platform).QueryRows(&knowledgeBases)
if len(knowledgeBases) > 0 {
bizType = "knowledge"
messageContentByte, _ := json.Marshal(knowledgeBases)
messageContent = string(messageContentByte)
} else {
// 没有找到再次找副标题
_, _ = o.Raw("SELECT title,sub_title FROM knowledge_base WHERE sub_title LIKE ? AND platform IN (?,?) ORDER by rand() limit 4", "|%"+message.Payload+"%|", 1, userData.Platform).QueryRows(&knowledgeBases)
if len(knowledgeBases) > 0 {
bizType = "knowledge"
messageContentByte, _ := json.Marshal(knowledgeBases)
messageContent = string(messageContentByte)
} else {
// 默认关键词
var subTitle = ""
for _, value := range roobtKeyWords {
subTitle = subTitle + " sub_title LIKE '%" + value + "%' OR "
}
subTitle = subTitle[1 : len(subTitle)-3]
_, _ = o.Raw("SELECT title,sub_title FROM knowledge_base WHERE ("+subTitle+") AND platform IN (?,?) ORDER by rand() limit 4", 1, userData.Platform).QueryRows(&knowledgeBases)
if len(knowledgeBases) > 0 {
bizType = "knowledge"
messageContentByte, _ := json.Marshal(knowledgeBases)
messageContent = string(messageContentByte)
} else {
messageContent = understand
}
}
}
}
}
}
// 消息体
callbackMessage.BizType = bizType
callbackMessage.FromAccount = message.ToAccount
callbackMessage.Timestamp = time.Now().Unix() + 1
callbackMessage.ToAccount = message.FromAccount
callbackMessage.Payload = messageContent
messageJSON, _ := json.Marshal(callbackMessage)
messageString := base64.StdEncoding.EncodeToString([]byte(messageJSON))
// 发给用户
robot.SendMessage(strconv.FormatInt(message.FromAccount, 10), []byte(messageString))
// 消息入库
MessageInto(callbackMessage, false)
}
package im
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"strings"
"github.com/astaxie/beego"
)
// TokenHandler ...
type TokenHandler struct {
httpURL string
AppID int64 `json:"appId"`
AppKey string `json:"appKey"`
AppSecret string `json:"appSecret"`
AppAccount string `json:"appAccount"`
}
// GetMiMcToken ...
func GetMiMcToken(accountID string) (string, error) {
tokenHandler := new(TokenHandler)
tokenHandler.httpURL = beego.AppConfig.String("mimc_HttpUrl")
tokenHandler.AppID, _ = beego.AppConfig.Int64("mimc_appId")
tokenHandler.AppKey = beego.AppConfig.String("mimc_appKey")
tokenHandler.AppSecret = beego.AppConfig.String("mimc_appSecret")
tokenHandler.AppAccount = accountID
jsonBytes, err := json.Marshal(*tokenHandler)
if err != nil {
return "", err
}
requestJSONBody := bytes.NewBuffer(jsonBytes).String()
request, err := http.Post(tokenHandler.httpURL, "application/json", strings.NewReader(requestJSONBody))
if err != nil {
return "", err
}
defer request.Body.Close()
body, err := ioutil.ReadAll(request.Body)
if err != nil {
return "", err
}
token := string(body)
return token, nil
}
package im
import (
"kefu_server/models"
"strconv"
"strings"
"github.com/Xiaomi-mimc/mimc-go-sdk"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
)
// Robots 工作中的机器人
var Robots []*mimc.MCUser
// CreateRobot 创建机器人
func CreateRobot(appAccount string) *mimc.MCUser {
appID, _ := beego.AppConfig.Int64("mimc_appId")
mcUser := mimc.NewUser(uint64(appID), appAccount)
mcUser.RegisterStatusDelegate(NewStatusHandler(appAccount))
mcUser.RegisterTokenDelegate(NewTokenHandler(appAccount))
mcUser.RegisterMessageDelegate(NewMsgHandler(appAccount))
mcUser.InitAndSetup()
return mcUser
}
// GetRobots get robot all
func GetRobots() []models.Robot {
// orm instance
o := orm.NewOrm()
robot := new(models.Robot)
qs := o.QueryTable(robot)
// 查询
var lists []models.Robot
_, _ = qs.OrderBy("-create_at").All(&lists)
for index := range lists {
lists[index].Artificial = strings.Trim(lists[index].Artificial, "|")
}
return lists
}
// RobotInit 初始化机器人
func RobotInit() {
// 如果有机器人在工作先退出登录
if len(Robots) > 0 {
for _, robot := range Robots {
robot.Logout()
robot.Destory()
}
Robots = []*mimc.MCUser{}
}
robotsData := GetRobots()
var tempRobots []*mimc.MCUser
for _, robot := range robotsData {
if robot.Switch == 1 {
rb := CreateRobot(strconv.FormatInt(robot.ID, 10))
tempRobots = append(tempRobots, rb)
rb.Login()
}
}
Robots = tempRobots
}
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/Xiaomi-mimc/mimc-go-sdk/util/log"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/toolbox"
_ "github.com/go-sql-driver/mysql"
"kefu_server/controllers"
"kefu_server/im"
"kefu_server/models"
_ "kefu_server/routers"
"strconv"
"time"
)
// init Mysql DB
func initDB() {
// 链接IM数据库
imAliasName := beego.AppConfig.String("im_alias_name")
imDriverName := beego.AppConfig.String("im_driver_name")
var imDataSource string
imDataSource = beego.AppConfig.String("im_mysql_user") + ":"
imDataSource += beego.AppConfig.String("im_mysql_pwd")
imDataSource += "@tcp(" + beego.AppConfig.String("im_mysql_host") + ":3306" + ")/"
imDataSource += beego.AppConfig.String("im_mysql_db") + "?charset=utf8"
_ = orm.RegisterDataBase(imAliasName, imDriverName, imDataSource, 30)
// 注册模型
orm.RegisterModel(new(models.User))
orm.RegisterModel(new(models.Admin))
orm.RegisterModel(new(models.Platform))
orm.RegisterModel(new(models.KnowledgeBase))
orm.RegisterModel(new(models.Robot))
orm.RegisterModel(new(models.Message))
orm.RegisterModel(new(models.System))
orm.RegisterModel(new(models.Shortcut))
orm.RegisterModel(new(models.Contact))
orm.RegisterModel(new(models.Company))
orm.RegisterModel(new(models.QiniuSetting))
orm.RegisterModel(new(models.UploadsConfig))
orm.RegisterModel(new(models.ServicesStatistical))
// 创建表
_ = orm.RunSyncdb("default", false, true)
}
// 初始化日志
func initLog() {
// 初始化日志
if isDev := beego.AppConfig.String("runmode"); isDev == "prod" {
log.SetLogLevel(log.FatalLevel)
_ = logs.SetLogger(logs.AdapterFile, `{"filename":"project.log","level":7,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10,"color":true}`)
fmt.Print("当前环境为生产环境")
_ = beego.BeeLogger.DelLogger("console")
} else {
log.SetLogLevel(log.ErrorLevel)
_ = logs.SetLogger(logs.AdapterConsole, `{"filename":"test.log","level":7,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10,"color":true}`)
fmt.Print("当前环境为测试环境")
}
logs.EnableFuncCallDepth(true)
}
// 定时任务
func appTask() {
o := orm.NewOrm()
// 任务调度(1分钟会执行一次)
checkOnLineTk := toolbox.NewTask("checkOnLine", "0 */1 * * * *", func() error {
userOffLineUnixTimer := time.Now().Unix() - (60 * 10) // 用户最后活动时间T出在线状态规则
adminOffLineUnixTimer := time.Now().Unix() - (60 * 30) // 最后回复消息时间清理回话规则
lastMessageUnixTimer := time.Now().Unix() - (60 * 8) // 判断用户是否超过一定时间不使用,强制其下线
uqs := o.QueryTable(new(models.User))
aqs := o.QueryTable(new(models.Admin))
// 检查User
count, _ := uqs.Filter("online__in", 1, 2).Filter("last_activity__lte", lastMessageUnixTimer).Update(orm.Params{
"online": 0,
"is_window": 0,
})
logs.Info("清理登录超时用户,有", count, "个用户被强制下线")
// 检查Admin
_, _ = aqs.Filter("online__in", 1, 2).Filter("last_activity__lte", adminOffLineUnixTimer).Update(orm.Params{
"online": 0,
})
// 2.判断用户是否超时无应答
cqs := o.QueryTable(new(models.Contact))
// 检查(有机器人在线)
if len(im.Robots) > 0 {
robot := im.Robots[0]
var contacts []models.Contact
_, _ = o.Raw("SELECT * FROM `contact` WHERE `create_at` <= ? AND `is_session_end` = 0 AND `last_message_type` != 'timeout'", lastMessageUnixTimer).QueryRows(&contacts)
logs.Info("清理会话超时用户,有", len(contacts), "个用户被结束对话")
for _, contact := range contacts {
// 判断发送方是客服就不处理发送了
if err := o.Read(&models.Admin{ID: contact.FromAccount}); err == nil {
continue
}
// 发送超时消息体
message := models.Message{}
message.BizType = "timeout"
message.Read = 0
appAccount, _ := strconv.ParseInt(robot.AppAccount(), 10, 64)
message.FromAccount = appAccount
message.Timestamp = time.Now().Unix()
message.Payload = "由于您长时间未回复,本次会话超时了"
message.ToAccount = contact.FromAccount
var messageJSON []byte
var messageString string
messageJSON, _ = json.Marshal(message)
messageString = base64.StdEncoding.EncodeToString([]byte(messageJSON))
robot.SendMessage(strconv.FormatInt(contact.FromAccount, 10), []byte(messageString))
// 该客户超时后给客服发送提醒消息
message.FromAccount = contact.FromAccount
message.ToAccount = contact.ToAccount
messageJSON, _ = json.Marshal(message)
messageString = base64.StdEncoding.EncodeToString([]byte(messageJSON))
robot.SendMessage(strconv.FormatInt(contact.ToAccount, 10), []byte(messageString))
im.MessageInto(message, true)
// 超时后消息
// 数据库获取机器人配置信息
robotData := models.Robot{ID: appAccount}
cacheRobotKey := "robot_" + string(appAccount)
if robotDataTemp := im.BmCache.Get(cacheRobotKey); robotDataTemp == nil {
robotData = models.Robot{ID: appAccount}
_ = o.Read(&robotData)
robotDataJSON, _ := json.Marshal(robotData)
_ = im.BmCache.Put(cacheRobotKey, robotDataJSON, 60*time.Second)
} else {
_ = json.Unmarshal([]byte(string(robotDataTemp.([]byte))), &robotData)
}
if robotData.TimeoutText != "" {
message.FromAccount = appAccount
message.ToAccount = contact.FromAccount
message.BizType = "text"
message.Payload = robotData.TimeoutText
messageJSON, _ = json.Marshal(message)
messageString = base64.StdEncoding.EncodeToString([]byte(messageJSON))
robot.SendMessage(strconv.FormatInt(contact.FromAccount, 10), []byte(messageString))
}
}
} else {
// 执行到这里说明,机器人死掉了
logs.Error("执行到这里说明,机器人死掉了")
im.RobotInit()
_, _ = cqs.Filter("create_at__lte", userOffLineUnixTimer).Update(orm.Params{
"last_message_type": "timeout",
"is_session_end": 1,
})
}
return nil
})
toolbox.AddTask("checkOnLine", checkOnLineTk)
}
func main() {
// 初始化数据库
initDB()
// 初始化日志
initLog()
// 启动任务
appTask()
toolbox.StartTask()
defer toolbox.StopTask()
/// 静态文件配置
beego.SetStaticPath("/", "public/client")
beego.SetStaticPath("/admin", "public/admin")
beego.SetStaticPath("/static", "static")
// 错误处理
beego.ErrorController(&controllers.ErrorController{})
// 启动机器人
im.RobotInit()
// 初始化beeGo
beego.Run()
}
package models
// Admin 默认值不生效请自行到mysql修改表结构哦
type Admin struct {
ID int64 `orm:"auto;pk;column(id)" json:"id"` // 客服(管理员)ID
Avatar string `orm:"type(char);column(avatar)" json:"avatar"` // 头像
UserName string `orm:"unique;type(char);column(username)" json:"username"` // 账号(用于登录)这个字段用错名词,将就用吧
NickName string `orm:"type(char);column(nickname)" json:"nickname"` // 昵称
Password string `orm:"type(char);column(password)" json:"password"` // 密码MD5
Phone string `orm:"type(char);null;column(phone)" json:"phone"` // 手机
Token string `orm:"null;type(text);column(token)" json:"token"` // token
AutoReply string `orm:"null;default('您好有什么可以帮到您呢?');type(text);column(auto_reply)" json:"auto_reply"` // 自动回复语
Online int `orm:"default(0);column(online)" json:"online"` // 在线状态 0 离线 1 在线 2 繁忙(繁忙状态不接待新客户)
Root int `orm:"default(0);column(root)" json:"root"` // 是否是超级管理员
CurrentConUser int64 `orm:"default(0);column(current_con_user)" json:"current_con_user"` // 当前连线的用户
LastActivity int64 `orm:"type(bigint);column(last_activity)" json:"last_activity"` // 最后活动时间
UpdateAt int64 `orm:"type(bigint);column(update_at)" json:"update_at"` // 资料更新时间
CreateAt int64 `orm:"type(bigint);column(create_at)" json:"create_at"` // 账号创建时间
}
package models
// Company struct
type Company struct {
ID int64 `orm:"auto;pk;type(bigint);column(id)" json:"id"` // id
Title string `orm:"null;type(char);column(title)" json:"title"` // 公司名称
Logo string `orm:"null;type(char);column(logo)" json:"logo"` // 公司logo
Service string `orm:"null;column(service)" json:"service"` // 在线客服服务时间
Email string `orm:"null;column(email)" json:"email"` // 公司邮箱
Tel string `orm:"null;column(tel)" json:"tel"` // 公司电话
Address string `orm:"null;type(char);column(address)" json:"address"` // 公司地址
UpdateAt int64 `orm:"type(bigint);column(update_at)" json:"update_at"`
}
package models
// Contact 通讯录
type Contact struct {
ID int64 `orm:"auto;pk;column(id)" json:"id"`
FromAccount int64 `orm:"type(bigint);column(from_account)" json:"from_account"`
ToAccount int64 `orm:"type(bigint);column(to_account)" json:"to_account"`
IsSessionEnd int `orm:"default(0),column(is_session_end)" json:"is_session_end"` // 1 已结束对话 0 未结束对话
LastMessage string `orm:"null;type(text);column(last_message)" json:"last_message"`
Delete int `orm:"default(0);column(delete)" json:"delete"` // 1 已删除 0 未删除
LastMessageType string `orm:"null;type(text);column(last_message_type)" json:"last_message_type"`
CreateAt int64 `orm:"type(bigint);column(create_at)" json:"create_at"`
}
package models
// ContactData struct
type ContactData struct {
ID int64 `orm:"column(id)" json:"id"`
Cid int64 `json:"cid"`
FromAccount int64 `json:"from_account"`
ToAccount int64 `json:"to_account"`
LastMessage string `json:"last_message"`
IsSessionEnd int `json:"is_session_end"`
LastMessageType string `json:"last_message_type"`
UID int64 `orm:"column(uid)" json:"uid"`
Avatar string `json:"avatar"`
Address string `json:"address"`
Nickname string `json:"nickname"`
Phone string `json:"phone"`
Platform int64 `json:"platform"`
Online int `json:"online"`
Read int `json:"read"`
UpdateAt int64 `json:"update_at"`
Remarks string `json:"remarks"`
LastActivity int64 `json:"last_activity"`
CreateAt int64 `json:"create_at"`
ContactCreateAt int64 `json:"contact_create_at"`
}
package models
// IMToken struct
type IMToken struct {
Code int `json:"code"`
Message string `json:"message"`
Data IMTokenData `json:"data"`
}
// IMTokenData struct
type IMTokenData struct {
AppID string `json:"appId"`
AppPackage string `json:"appPackage"`
AppAccount string `json:"appAccount"`
MiChid int64 `json:"miChid"`
MiUserID string `json:"miUserId"`
MiUserSecurityKey string `json:"miUserSecurityKey"`
Token string `json:"token"`
RegionBucket int64 `json:"regionBucket"`
FeDomainName string `json:"feDomainName"`
RelayDomainName string `json:"relayDomainName"`
}
package models
// KnowledgeBase struct
type KnowledgeBase struct {
ID int64 `orm:"auto;pk;type(bigint);column(id)" json:"id"`
UID int64 `orm:"type(bigint);column(uid)" json:"uid"`
Title string `orm:"unique;type(char);column(title)" json:"title"`
SubTitle string `orm:"null;type(content);column(sub_title)" json:"sub_title"`
Content string `orm:"null;type(text);column(content)" json:"content"`
Platform int64 `orm:"default(1),type(bigint);column(platform)" json:"platform"` // 0是匹配所有
UpdateAt int64 `orm:"type(bigint);column(update_at)" json:"update_at"`
CreateAt int64 `orm:"type(bigint);column(create_at)" json:"create_at"`
}
package models
// Message struct
// BizType 消息类型 先以字符串形式声明吧
// 1 video 视频
// 2 text 文本
// 3 photo 图片
// 4 transfer 转接
// 5 knowledge 知识内容
// 6 handshake 用户请求与握手
// 7 pong 对方正在输入中
// 8 end 结束消息
// 9 timeout 超时会话关闭
// 10 cancel 撤回消息(暂时还没做预留先)
// 11 contacts 用户列表(客服端)
// 12 welcome 欢迎语
// 13 system 系统消息
// 14 search_knowledge 检索关键词知识库消息
type Message struct {
ID int64 `orm:"auto;pk;column(id)" json:"id"` // 消息ID
FromAccount int64 `orm:"type(bigint);column(from_account)" json:"from_account"` // 发送人账号
ToAccount int64 `orm:"type(bigint);column(to_account)" json:"to_account"` // 接收人账号
BizType string `orm:"type(char);column(biz_type)" json:"biz_type"` // 消息类型
Version string `orm:"default(0);type(char);column(version)" json:"version"` // 版本号,预留暂时无用
Timestamp int64 `orm:"type(bigint);column(timestamp)" json:"timestamp"` // 服务端消息消息时间
Key int64 `orm:"type(bigint);column(key)" json:"key"` // key
TransferAccount int64 `orm:"type(bigint);column(transfer_account)" json:"transfer_account"` // 转接到客户的账号
Platform int64 `orm:"type(bigint);column(platform)" json:"platform"` // 此消息来自哪个平台(即渠道)
Payload string `orm:"null;type(text);column(payload)" json:"payload"` // 消息内容
Read int `orm:"default(1);column(read)" json:"read"` // 是否已读消息0已读1未读
Delete int `orm:"default(0);column(delete)" json:"delete"` // 是否已删除消息0 ro 1 1已删除
}
package models
// PaginationData struct
type PaginationData struct {
PageSize int `json:"page_size"`
PageOn int `json:"page_on"`
Keyword string `json:"keyword"`
Total int64 `json:"total"`
List interface{} `json:"list"`
}
package models
// Platform struct
type Platform struct {
ID int64 `orm:"auto;pk;type(bigint);column(id)" json:"id"`
Title string `orm:"unique;type(char);column(title)" json:"title"`
Alias string `orm:"unique;type(char);column(alias)" json:"alias"`
System int `orm:"default(0);column(system)" json:"system"`
}
//INSERT INTO `kefu_server`.`platform`(`id`, `title`, `alias`, `system`) VALUES (0, '全平台', 'all', 1);
//INSERT INTO `kefu_server`.`platform`(`id`, `title`, `alias`, `system`) VALUES (1, 'IOS', 'ios', 1);
//INSERT INTO `kefu_server`.`platform`(`id`, `title`, `alias`, `system`) VALUES (2, '小程序', 'small', 1);
//INSERT INTO `kefu_server`.`platform`(`id`, `title`, `alias`, `system`) VALUES (3, 'PC网页', 'pc', 1);
//INSERT INTO `kefu_server`.`platform`(`id`, `title`, `alias`, `system`) VALUES (4, '移动网页', 'mobile', 1);
//INSERT INTO `kefu_server`.`platform`(`id`, `title`, `alias`, `system`) VALUES (5, 'Android', 'android', 1);
package models
// QiniuSetting struct
type QiniuSetting struct {
ID int64 `orm:"auto;pk;column(id)" json:"id"`
Bucket string `orm:"type(char);column(bucket)" json:"bucket"`
AccessKey string `orm:"unique;type(char);column(access_key)" json:"access_key"`
SecretKey string `orm:"type(char);column(secret_key)" json:"secret_key"`
Host string `orm:"type(char);column(host)" json:"host"`
UpdateAt int64 `orm:"type(bigint);column(update_at)" json:"update_at"`
}
package models
// Response struct
type Response struct {
Code int `json:"code"` // 错误类型
Message string `json:"message"` // 信息
Data interface{} `json:"data"` // 任意类型
}
package models
// Robot struct
type Robot struct {
ID int64 `orm:"auto;pk;type(bigint);column(id)" json:"id"`
NickName string `orm:"unique;type(char);column(nickname)" json:"nickname"`
Avatar string `orm:"type(char);column(avatar)" json:"avatar"`
Welcome string `orm:"column(welcome)" json:"welcome"` // 欢迎语
Understand string `orm:"column(understand)" json:"understand"` // 不明白语句
Artificial string `orm:"column(artificial)" json:"artificial"` // 关键词转人工
KeyWord string `orm:"column(keyword)" json:"keyword"` // 知识库默认匹配词
TimeoutText string `orm:"column(timeout_text)" json:"timeout_text"` // 超时提示语
NoServices string `orm:"column(no_services)" json:"no_services"` // 无人工在线提示语
LoogTimeWaitText string `orm:"column(loog_time_wait_text)" json:"loog_time_wait_text"` // 长时间等待提示
Switch int `orm:"default(0);column(switch)" json:"switch"` // 是否开启
System int `orm:"default(0);column(system)" json:"system"` // 系统内置
Platform int64 `orm:"type(bigint);column(platform)" json:"platform"` // 服务那个平台
UpdateAt int64 `orm:"type(bigint);column(update_at)" json:"update_at"`
CreateAt int64 `orm:"auto_now_add;type(int64);null;column(create_at)" json:"create_at"`
}
package models
// ServicesStatistical 转接一次计算一次服务次数
type ServicesStatistical struct {
ID int64 `orm:"auto;pk;column(id)" json:"id"` // ID
UserAccount int64 `orm:"type(bigint);column(user_account)" json:"user_account"` // 服务对象ID
ServiceAccount int64 `orm:"type(bigint);column(service_account)" json:"service_account"` // 服务者ID
TransferAccount int64 `orm:"type(bigint);column(transfer_account)" json:"transfer_account"` // 转接者ID
Platform int64 `orm:"type(bigint);column(platform)" json:"platform"` // 此用户来自哪个平台(即渠道)
NickName string `orm:"type(char);null;column(nickname)" json:"nickname"` // 用户昵称
Satisfaction int `orm:"default(0);column(satisfaction)" json:"satisfaction"` // 满意度1-5
CreateAt int64 `orm:"type(bigint);column(create_at)" json:"create_at"` // 创建时间
}
package models
// SessionRequest 会话资料
// type 用户类型 0 | 1 0 = 用户 1 = 客服
// uid 自身业务平台用户ID
// account_id 用户客服ID,用户在mimc的唯一ID
// platform 平台 1,2,3,4,5
type SessionRequest struct {
Type int `json:"type"`
UID int64 `orm:"column(uid)" json:"uid"`
Platform int64 `json:"platform"`
Address string `json:"address"`
AccountID int64 `orm:"column(account_id)" json:"account_id"`
}
package models
// Shortcut 快捷语
type Shortcut struct {
ID int64 `orm:"auto;pk;column(id)" json:"id"`
UID int64 `orm:"type(bigint);column(uid)" json:"uid"`
Title string `orm:"type(text);null;column(title)" json:"title"`
Content string `orm:"type(text);null;column(content)" json:"content"`
UpdateAt int64 `orm:"type(bigint);column(update_at)" json:"update_at"`
CreateAt int64 `orm:"type(bigint);column(create_at)" json:"create_at"`
}
package models
// System struct
type System struct {
ID int64 `orm:"auto;pk;type(bigint);column(id)" json:"id"` // id
Title string `orm:"type(char);column(title)" json:"title"`
Logo string `orm:"type(char);column(logo)" json:"logo"`
CopyRight string `orm:"column(copy_right)" json:"copy_right"`
UploadMode int `orm:"column(upload_mode)" json:"upload_mode"` // 1 七牛 目前只实现七牛,其它的后续可以扩展
UpdateAt int64 `orm:"type(bigint);column(update_at)" json:"update_at"`
}
package models
// UploadsConfig struct
type UploadsConfig struct {
ID int64 `orm:"auto;pk;column(id)" json:"id"`
Name string `orm:"type(char);column(name)" json:"name"`
}
package models
// User struct
type User struct {
ID int64 `orm:"auto;pk;type(bigint);column(id)" json:"id"` // 用户ID
UID int64 `orm:"type(bigint);column(uid)" json:"uid"` // 对应业务平台的用户ID(保留字段)
Avatar string `orm:"type(char);null;column(avatar)" json:"avatar"` // 用户头像
Address string `orm:"type(char);null;column(address)" json:"address"` // 用户所在地
NickName string `orm:"type(char);null;column(nickname)" json:"nickname"` // 用户昵称
UserToken string `orm:"type(text);null;column(token)" json:"token"` // 对应业务平台的用户的token(保留字段)
Phone string `orm:"type(char);null;column(phone)" json:"phone"` // 用户联系电话
Platform int64 `orm:"type(bigint);column(platform)" json:"platform"` // 用户所在渠道(平台)
Online int `orm:"default(0);column(online)" json:"online"` // 用户是否在线
IsWindow int `orm:"default(0);column(is_window)" json:"is_window"` // 是否在聊天窗口
UpdateAt int64 `orm:"type(bigint);column(update_at)" json:"update_at"` // 用户资料被更新时间
Remarks string `orm:"type(char);null;column(remarks)" json:"remarks"` // 备注
LastActivity int64 `orm:"type(bigint);column(last_activity)" json:"last_activity"` // 最后活动时间
CreateAt int64 `orm:"type(bigint);column(create_at)" json:"create_at"` // 创建时间
}
This diff could not be displayed because it is too large.
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory(global):typeof define==="function"&&define.amd?define(factory):factory(global)})(typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:this,function(global){"use strict";global=global||{};var _Base64=global.Base64;var version="2.5.1";var buffer;if(typeof module!=="undefined"&&module.exports){try{buffer=eval("require('buffer').Buffer")}catch(err){buffer=undefined}}var b64chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var b64tab=function(bin){var t={};for(var i=0,l=bin.length;i<l;i++)t[bin.charAt(i)]=i;return t}(b64chars);var fromCharCode=String.fromCharCode;var cb_utob=function(c){if(c.length<2){var cc=c.charCodeAt(0);return cc<128?c:cc<2048?fromCharCode(192|cc>>>6)+fromCharCode(128|cc&63):fromCharCode(224|cc>>>12&15)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}else{var cc=65536+(c.charCodeAt(0)-55296)*1024+(c.charCodeAt(1)-56320);return fromCharCode(240|cc>>>18&7)+fromCharCode(128|cc>>>12&63)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}};var re_utob=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;var utob=function(u){return u.replace(re_utob,cb_utob)};var cb_encode=function(ccc){var padlen=[0,2,1][ccc.length%3],ord=ccc.charCodeAt(0)<<16|(ccc.length>1?ccc.charCodeAt(1):0)<<8|(ccc.length>2?ccc.charCodeAt(2):0),chars=[b64chars.charAt(ord>>>18),b64chars.charAt(ord>>>12&63),padlen>=2?"=":b64chars.charAt(ord>>>6&63),padlen>=1?"=":b64chars.charAt(ord&63)];return chars.join("")};var btoa=global.btoa?function(b){return global.btoa(b)}:function(b){return b.replace(/[\s\S]{1,3}/g,cb_encode)};var _encode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(u){return(u.constructor===buffer.constructor?u:buffer.from(u)).toString("base64")}:function(u){return(u.constructor===buffer.constructor?u:new buffer(u)).toString("base64")}:function(u){return btoa(utob(u))};var encode=function(u,urisafe){return!urisafe?_encode(String(u)):_encode(String(u)).replace(/[+\/]/g,function(m0){return m0=="+"?"-":"_"}).replace(/=/g,"")};var encodeURI=function(u){return encode(u,true)};var re_btou=new RegExp(["[À-ß][€-¿]","[à-ï][€-¿]{2}","[ð-÷][€-¿]{3}"].join("|"),"g");var cb_btou=function(cccc){switch(cccc.length){case 4:var cp=(7&cccc.charCodeAt(0))<<18|(63&cccc.charCodeAt(1))<<12|(63&cccc.charCodeAt(2))<<6|63&cccc.charCodeAt(3),offset=cp-65536;return fromCharCode((offset>>>10)+55296)+fromCharCode((offset&1023)+56320);case 3:return fromCharCode((15&cccc.charCodeAt(0))<<12|(63&cccc.charCodeAt(1))<<6|63&cccc.charCodeAt(2));default:return fromCharCode((31&cccc.charCodeAt(0))<<6|63&cccc.charCodeAt(1))}};var btou=function(b){return b.replace(re_btou,cb_btou)};var cb_decode=function(cccc){var len=cccc.length,padlen=len%4,n=(len>0?b64tab[cccc.charAt(0)]<<18:0)|(len>1?b64tab[cccc.charAt(1)]<<12:0)|(len>2?b64tab[cccc.charAt(2)]<<6:0)|(len>3?b64tab[cccc.charAt(3)]:0),chars=[fromCharCode(n>>>16),fromCharCode(n>>>8&255),fromCharCode(n&255)];chars.length-=[0,0,2,1][padlen];return chars.join("")};var _atob=global.atob?function(a){return global.atob(a)}:function(a){return a.replace(/\S{1,4}/g,cb_decode)};var atob=function(a){return _atob(String(a).replace(/[^A-Za-z0-9\+\/]/g,""))};var _decode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(a){return(a.constructor===buffer.constructor?a:buffer.from(a,"base64")).toString()}:function(a){return(a.constructor===buffer.constructor?a:new buffer(a,"base64")).toString()}:function(a){return btou(_atob(a))};var decode=function(a){return _decode(String(a).replace(/[-_]/g,function(m0){return m0=="-"?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))};var noConflict=function(){var Base64=global.Base64;global.Base64=_Base64;return Base64};global.Base64={VERSION:version,atob:atob,btoa:btoa,fromBase64:decode,toBase64:encode,utob:utob,encode:encode,encodeURI:encodeURI,btou:btou,decode:decode,noConflict:noConflict,__buffer__:buffer};if(typeof Object.defineProperty==="function"){var noEnum=function(v){return{value:v,enumerable:false,writable:true,configurable:true}};global.Base64.extendString=function(){Object.defineProperty(String.prototype,"fromBase64",noEnum(function(){return decode(this)}));Object.defineProperty(String.prototype,"toBase64",noEnum(function(urisafe){return encode(this,urisafe)}));Object.defineProperty(String.prototype,"toBase64URI",noEnum(function(){return encode(this,true)}))}}if(global["Meteor"]){Base64=global.Base64}if(typeof module!=="undefined"&&module.exports){module.exports.Base64=global.Base64}else if(typeof define==="function"&&define.amd){define([],function(){return global.Base64})}return{Base64:global.Base64}});
\ No newline at end of file
#app{display:-ms-flexbox;display:flex;height:100vh}.el-tab-pane,.el-tabs__content{height:100%;padding:0}.el-tabs__content{padding:0!important}button{background-color:#fff}.pswp{z-index:3000!important}
\ No newline at end of file
.mini-im-aside{background-color:#3e444a}.mini-im-aside,.mini-im-aside .mini-im-logo{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.mini-im-aside .mini-im-logo{width:100%;height:100px;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;border-bottom:1px solid #ddd}.mini-im-aside .mini-im-logo img{height:30px}.mini-im-aside .el-menu{border-right:0}.mini-im-aside .el-badge__content{border:0;top:30px}.mini-im-aside .fix-bottom{-ms-flex-positive:1;flex-grow:1;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:end;justify-content:flex-end;padding-bottom:30px}.mini-im-aside .fix-bottom a{cursor:pointer;padding:0 30px;text-align:center;-ms-flex-align:center;align-items:center;color:#fff;display:-ms-flexbox;display:flex;-ms-flex-line-pack:center;align-content:center}.mini-im-aside .fix-bottom a .github-log{width:50px}.mini-im-aside .fix-bottom a span{margin-top:3px;margin-left:5px}.mini-im-header[data-v-432b9ecd]{background-color:#545c64;border-bottom:1px solid #545c64}.mini-im-header .mini-im-dropdown[data-v-432b9ecd]{height:100%}.mini-im-header .mini-im-button[data-v-432b9ecd]{border:0;font-size:35px;display:block;background:0;padding-left:0}.mini-im-header .mini-im-title[data-v-432b9ecd]{color:#fff;font-size:16px;text-align:center;line-height:60px}.mini-im-header .icon[data-v-432b9ecd]{color:#fff}.mini-im-header .el-dropdown-link[data-v-432b9ecd]{cursor:pointer;display:-ms-flexbox;display:flex;height:100%;line-height:60px;-ms-flex-align:center;align-items:center;color:#fff}.mini-im-header .el-icon-arrow-down[data-v-432b9ecd]{font-size:12px}.mini-im-file-button[data-v-0d4bcba3]{width:50px;height:50px;border-radius:50%;position:relative;overflow:hidden;cursor:pointer}.mini-im-file-button input[data-v-0d4bcba3]{font-size:100px;position:absolute;top:0;left:0;cursor:pointer;opacity:0}.mini-im-file-button .mini-im-file-percent[data-v-0d4bcba3]{position:absolute;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;background-color:rgba(0,0,0,.5);color:#fff;font-size:12px}.mini-im-header[data-v-61095d10]{background-color:#545c64;border-bottom:1px solid #545c64}
\ No newline at end of file
.mini-im-chat-list .mini-im-chat-message-box[data-v-3d81ef40],.mini-im-chat-list[data-v-3d81ef40]{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.mini-im-chat-list .mini-im-chat-message-box[data-v-3d81ef40]{width:100%;margin-bottom:15px}.mini-im-chat-list .mini-im-chat-message-box .user-date[data-v-3d81ef40]{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;color:#999;font-size:14px}.mini-im-chat-list .mini-im-chat-message-box .user-date span[data-v-3d81ef40]{color:#666;font-weight:500;font-size:14px;padding:0 5px}.mini-im-chat-list .mini-im-chat-message-box .user-date em[data-v-3d81ef40]{font-style:normal;font-size:12px}.mini-im-chat-list .mini-im-chat-message-box .loading[data-v-3d81ef40]{color:#666;display:-ms-flexbox;display:flex;margin-top:5px;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;-ms-flex-pack:center;justify-content:center}.mini-im-chat-list .mini-im-chat-message-box .loading span[data-v-3d81ef40]{margin-left:5px;font-size:13px}.mini-im-chat-list .mini-im-chat-message-box .text[data-v-3d81ef40]{display:-ms-flexbox;display:flex;margin-top:5px}.mini-im-chat-list .mini-im-chat-message-box .text span[data-v-3d81ef40]{max-width:40%;display:inline;padding:5px 10px;border-radius:5px;background-color:#eef4f9;font-size:14px;color:#666}.mini-im-chat-list .mini-im-chat-message-box .photo[data-v-3d81ef40]{display:-ms-flexbox;display:flex;margin-top:5px}.mini-im-chat-list .mini-im-chat-message-box .photo .loading[data-v-3d81ef40]{-ms-flex-item-align:end;align-self:flex-end;padding:0 5px}.mini-im-chat-list .mini-im-chat-message-box .photo .loading span[data-v-3d81ef40]{background:none!important;color:#999!important}.mini-im-chat-list .mini-im-chat-message-box .photo .img-content[data-v-3d81ef40]{border-radius:5px;width:200px;overflow:hidden}.mini-im-chat-list .mini-im-chat-message-box .photo img[data-v-3d81ef40]{cursor:pointer;width:100%;height:100%;display:inline}.mini-im-chat-list .mini-im-chat-message-box .knowledge[data-v-3d81ef40]{display:-ms-flexbox;display:flex;margin-top:5px;-ms-flex-pack:end;justify-content:flex-end}.mini-im-chat-list .mini-im-chat-message-box .knowledge .content[data-v-3d81ef40]{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding:5px;border-radius:5px;color:#666;text-align:left;background-color:#eef4f9}.mini-im-chat-list .mini-im-chat-message-box .knowledge .content .title[data-v-3d81ef40]{font-size:13px;font-weight:500}.mini-im-chat-list .mini-im-chat-message-box .knowledge .content .item[data-v-3d81ef40]{font-size:13px;line-height:22px}.mini-im-chat-list .mini-im-chat-message-box .system[data-v-3d81ef40]{display:-ms-flexbox;display:flex;margin-top:5px;-ms-flex-direction:column;flex-direction:column;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.mini-im-chat-list .mini-im-chat-message-box .system em[data-v-3d81ef40]{margin-top:5px;font-size:12px;color:#999}.mini-im-chat-list .mini-im-chat-message-box .system span[data-v-3d81ef40]{font-size:12px;max-width:50%;min-width:100px;display:inline;padding:3px 20px;border-radius:5px;text-align:center;background-color:#f2f2f2;color:#999}.mini-im-chat-list .mini-im-chat-message-box.self[data-v-3d81ef40]{text-align:right}.mini-im-chat-list .mini-im-chat-message-box.self .user-date[data-v-3d81ef40]{display:-ms-flexbox;display:flex;-ms-flex-pack:end;justify-content:flex-end}.mini-im-chat-list .mini-im-chat-message-box.self .user-date span[data-v-3d81ef40]{-ms-flex-order:-2;order:-2}.mini-im-chat-list .mini-im-chat-message-box.self .user-date em[data-v-3d81ef40]{-ms-flex-order:-3;order:-3}.mini-im-chat-list .mini-im-chat-message-box.self .photo[data-v-3d81ef40],.mini-im-chat-list .mini-im-chat-message-box.self .text[data-v-3d81ef40]{-ms-flex-pack:end;justify-content:flex-end;-ms-flex-align:end;align-items:flex-end}.mini-im-chat-list .mini-im-chat-message-box.self .photo .cancel-btn[data-v-3d81ef40],.mini-im-chat-list .mini-im-chat-message-box.self .text .cancel-btn[data-v-3d81ef40]{color:#26a2ff;font-size:12px;margin-right:5px;cursor:pointer}.mini-im-chat-list .mini-im-chat-message-box.self .photo span[data-v-3d81ef40],.mini-im-chat-list .mini-im-chat-message-box.self .text span[data-v-3d81ef40]{background-color:rgba(33,150,243,.72);color:#fff;text-align:left}.mini-im-chat-list .mini-im-chat-message-box.self .knowledge>.content[data-v-3d81ef40]{background-color:rgba(33,150,243,.72);color:#fff}.record-page .record-mini-im-head{height:30px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;font-size:20px;-ms-flex-pack:justify;justify-content:space-between;color:#666}.record-page .record-mini-im-head i{margin-right:5px}.record-page .record-modal-chat-box{height:600px;padding:0 20px;overflow:hidden;overflow-y:auto}.record-page .el-dialog__body{padding:0;border-top:1px solid #f7f7f7}
\ No newline at end of file
.login[data-v-27526a96]{display:-ms-flexbox;display:flex;width:100%;height:100%;background:url(../img/login_bg.8ba760be.jpg) bottom no-repeat;background-size:cover}.login .form[data-v-27526a96]{display:-ms-flexbox;display:flex;overflow:hidden;width:600px;height:300px;background-color:#fff;margin:auto;border-radius:5px}.login .form .left[data-v-27526a96]{width:350px;height:100%;padding:20px;box-sizing:border-box;background:url(../img/login_bg1.531e0c1c.jpg) bottom no-repeat;background-size:cover;font-size:18px;color:#fff;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center}.login .form .left .tit[data-v-27526a96]{margin-top:50px;border-bottom:1px solid #fff}.login .form .left div[data-v-27526a96]{width:300px;padding-bottom:10px;margin-bottom:10px}.login .form .left span[data-v-27526a96]{font-size:14px}.login .form .left .client[data-v-27526a96]{margin-top:80px}.login .form .left .client button[data-v-27526a96]{margin-top:10px}.login .form .right[data-v-27526a96]{padding:20px;padding-top:50px}.login .form .right .input[data-v-27526a96]{margin-bottom:20px}.login .form .right .btn-group[data-v-27526a96]{margin-top:15px}.login .form .right .lable[data-v-27526a96]{font-size:18px;color:#606266;margin-bottom:15px;display:block}
\ No newline at end of file
.mini-im-head[data-v-e4baa1e8]{height:60px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;font-size:20px;-ms-flex-pack:justify;justify-content:space-between;color:#666}.mini-im-head i[data-v-e4baa1e8]{margin-right:5px}.mini-im-file-button[data-v-e4baa1e8]{width:180px;position:relative;overflow:hidden;border-radius:3px;padding:5px;box-shadow:1px 1px 7px 0 #ccc;cursor:pointer}.mini-im-file-button input[data-v-e4baa1e8]{width:180px;font-size:100px;position:absolute;top:0;left:0;cursor:pointer;opacity:0}.mini-im-file-button img[data-v-e4baa1e8]{width:100%;display:block}.mini-im-file-button .mini-im-file-percent[data-v-e4baa1e8]{position:absolute;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;background-color:rgba(0,0,0,.5);color:#fff;font-size:12px}
\ No newline at end of file
.mini-im-file-button[data-v-0a24570c]{width:50px;height:50px;border-radius:50%;position:relative;overflow:hidden;cursor:pointer}.mini-im-file-button input[data-v-0a24570c]{font-size:100px;position:absolute;top:0;left:0;cursor:pointer;opacity:0}.mini-im-file-button .mini-im-file-percent[data-v-0a24570c]{position:absolute;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;background-color:rgba(0,0,0,.5);color:#fff;font-size:12px}.mini-im-head[data-v-0aee8f9a]{height:30px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;font-size:20px;-ms-flex-pack:justify;justify-content:space-between;color:#666}.mini-im-head i[data-v-0aee8f9a]{margin-right:5px}.el-select .el-input[data-v-0aee8f9a]{width:130px}.input-with-select .el-input-group__prepend[data-v-0aee8f9a]{background-color:#fff}
\ No newline at end of file
.mini-im-file-button[data-v-6bfc8d44]{width:50px;height:50px;border-radius:50%;position:relative;overflow:hidden;cursor:pointer}.mini-im-file-button input[data-v-6bfc8d44]{font-size:100px;position:absolute;top:0;left:0;cursor:pointer;opacity:0}.mini-im-file-button .mini-im-file-percent[data-v-6bfc8d44]{position:absolute;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;background-color:rgba(0,0,0,.5);color:#fff;font-size:12px}.mini-im-head[data-v-d2bb02c0]{height:30px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;font-size:20px;-ms-flex-pack:justify;justify-content:space-between;color:#666}.mini-im-head i[data-v-d2bb02c0]{margin-right:5px}
\ No newline at end of file
.mini-im-file-button[data-v-2e63f8e9]{width:50px;height:50px;border-radius:50%;position:relative;overflow:hidden;cursor:pointer}.mini-im-file-button input[data-v-2e63f8e9]{font-size:100px;position:absolute;top:0;left:0;cursor:pointer;opacity:0}.mini-im-file-button .mini-im-file-percent[data-v-2e63f8e9]{position:absolute;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;background-color:rgba(0,0,0,.5);color:#fff;font-size:12px}.el-tag+.el-tag[data-v-2e63f8e9]{margin-left:10px}.button-new-tag[data-v-2e63f8e9]{margin-left:10px;height:32px;line-height:30px;padding-top:0;padding-bottom:0}.input-new-tag[data-v-2e63f8e9]{width:150px;margin-left:10px;vertical-align:bottom}.mini-im-file-button[data-v-6b34f2e3]{width:50px;height:50px;border-radius:50%;position:relative;overflow:hidden;cursor:pointer}.mini-im-file-button input[data-v-6b34f2e3]{font-size:100px;position:absolute;top:0;left:0;cursor:pointer;opacity:0}.mini-im-file-button .mini-im-file-percent[data-v-6b34f2e3]{position:absolute;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;background-color:rgba(0,0,0,.5);color:#fff;font-size:12px}.el-tag+.el-tag[data-v-6b34f2e3]{margin-left:10px}.button-new-tag[data-v-6b34f2e3]{margin-left:10px;height:32px;line-height:30px;padding-top:0;padding-bottom:0}.input-new-tag[data-v-6b34f2e3]{width:150px;margin-left:10px;vertical-align:bottom}.mini-im-head[data-v-dea0f158]{height:30px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;font-size:20px;-ms-flex-pack:justify;justify-content:space-between;color:#666}.mini-im-head i[data-v-dea0f158]{margin-right:5px}
\ No newline at end of file
.mini-im-home-title[data-v-66efe601]{text-align:center;font-size:18px;color:#666;padding:15px 0 50px}.mini-im-home-copyright[data-v-66efe601]{text-align:center;color:#666;font-size:14px;padding-top:50px}.online-count[data-v-66efe601]{text-align:center;color:#666;margin-top:10px}.online-count span[data-v-66efe601]{color:#8bc34a;margin:0 5px}
\ No newline at end of file
.el-tag+.el-tag[data-v-015f50bf]{margin-left:10px}.button-new-tag[data-v-015f50bf]{margin-left:10px;height:32px;line-height:30px;padding-top:0;padding-bottom:0}.input-new-tag[data-v-015f50bf]{width:150px;margin-left:10px;vertical-align:bottom}.el-tag+.el-tag[data-v-7b12f7d6]{margin-left:10px}.button-new-tag[data-v-7b12f7d6]{margin-left:10px;height:32px;line-height:30px;padding-top:0;padding-bottom:0}.input-new-tag[data-v-7b12f7d6]{width:150px;margin-left:10px;vertical-align:bottom}.me-head[data-v-e8cecb14]{height:30px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;font-size:20px;-ms-flex-pack:justify;justify-content:space-between;color:#666}.me-head i[data-v-e8cecb14]{margin-right:5px}
\ No newline at end of file
This diff could not be displayed because it is too large.
No preview for this file type
<svg width="264" height="88" viewBox="0 0 264 88" xmlns="http://www.w3.org/2000/svg"><title>default-skin 2</title><g fill="none" fill-rule="evenodd"><g><path d="M67.002 59.5v3.768c-6.307.84-9.184 5.75-10.002 9.732 2.22-2.83 5.564-5.098 10.002-5.098V71.5L73 65.585 67.002 59.5z" id="Shape" fill="#fff"/><g fill="#fff"><path d="M13 29v-5h2v3h3v2h-5zM13 15h5v2h-3v3h-2v-5zM31 15v5h-2v-3h-3v-2h5zM31 29h-5v-2h3v-3h2v5z" id="Shape"/></g><g fill="#fff"><path d="M62 24v5h-2v-3h-3v-2h5zM62 20h-5v-2h3v-3h2v5zM70 20v-5h2v3h3v2h-5zM70 24h5v2h-3v3h-2v-5z"/></g><path d="M20.586 66l-5.656-5.656 1.414-1.414L22 64.586l5.656-5.656 1.414 1.414L23.414 66l5.656 5.656-1.414 1.414L22 67.414l-5.656 5.656-1.414-1.414L20.586 66z" fill="#fff"/><path d="M111.785 65.03L110 63.5l3-3.5h-10v-2h10l-3-3.5 1.785-1.468L117 59l-5.215 6.03z" fill="#fff"/><path d="M152.215 65.03L154 63.5l-3-3.5h10v-2h-10l3-3.5-1.785-1.468L147 59l5.215 6.03z" fill="#fff"/><g><path id="Rectangle-11" fill="#fff" d="M160.957 28.543l-3.25-3.25-1.413 1.414 3.25 3.25z"/><path d="M152.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" id="Oval-1" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M150 21h5v1h-5z"/></g><g><path d="M116.957 28.543l-1.414 1.414-3.25-3.25 1.414-1.414 3.25 3.25z" fill="#fff"/><path d="M108.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M106 21h5v1h-5z"/><path fill="#fff" d="M109.043 19.008l-.085 5-1-.017.085-5z"/></g></g></g></svg>
\ No newline at end of file
<!DOCTYPE html><html lang=zh><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=favicon.ico><script src=mimc-min_1_0_2.js></script><script src=base64.min.js></script><title>客服系统</title><style>body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td { margin:0; padding:0; }
body, button, input, select, textarea { font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif; }
h1, h2, h3, h4, h5, h6{ font-size:100%; }
address, cite, dfn, em, var { font-style:normal; }
code, kbd, pre, samp { font-family:couriernew, courier, monospace; }
small{ font-size:12px; }
ul, ol { list-style:none; }
a { text-decoration:none;
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
-webkit-user-select: none;
-moz-user-focus: none;
-moz-user-select: none;
}
a:hover { text-decoration:underline; }
sup { vertical-align:text-top; }
sub{ vertical-align:text-bottom; }
legend { color:#000; }
fieldset, img { border:0; }
button, input, select, textarea { font-size:100%; outline: none;}
table { border-collapse:collapse; border-spacing:0; }
input{
border:0;
outline: none;
}
body{
height: 100vh;
overflow: hidden;
overflow-y: auto;
-webkit-overflow-scrolling:touch;
background-color: #fff;
}
.lx-load-box{
width: 2rem !important;
height: 2rem !important;
top:0 !important;
min-height: inherit!important;
left:0 !important; right:0 !important; bottom:0 !important; margin: auto !important;
}
input::-webkit-input-placeholder{
color:#ccc;
}
input::-moz-placeholder{ /* Mozilla Firefox 19+ */
color:#ccc;
}
input:-moz-placeholder{ /* Mozilla Firefox 4 to 18 */
color:#ccc;
}
input:-ms-input-placeholder{ /* Internet Explorer 10-11 */
color:#ccc;
}</style><link href=css/chunk-0499e026.d2993177.css rel=prefetch><link href=css/chunk-08473d3a.77daa9fe.css rel=prefetch><link href=css/chunk-2e29b63f.82fa3580.css rel=prefetch><link href=css/chunk-35027968.4de0a346.css rel=prefetch><link href=css/chunk-3a5ae4b9.36eb5b7d.css rel=prefetch><link href=css/chunk-487f5713.329fd3ec.css rel=prefetch><link href=css/chunk-771e4186.92ee7235.css rel=prefetch><link href=css/chunk-99e15be2.2e3a70e9.css rel=prefetch><link href=css/chunk-b4735d10.9f747f60.css rel=prefetch><link href=css/chunk-e3edb682.0b5ced8d.css rel=prefetch><link href=js/chunk-0499e026.accfa348.js rel=prefetch><link href=js/chunk-08473d3a.eae62d86.js rel=prefetch><link href=js/chunk-2e29b63f.7ff0f22a.js rel=prefetch><link href=js/chunk-343c8d41.f0041881.js rel=prefetch><link href=js/chunk-35027968.79ffcf7b.js rel=prefetch><link href=js/chunk-3a5ae4b9.95fb80dc.js rel=prefetch><link href=js/chunk-487f5713.773eab50.js rel=prefetch><link href=js/chunk-771e4186.f1240d97.js rel=prefetch><link href=js/chunk-99e15be2.69d71101.js rel=prefetch><link href=js/chunk-b4735d10.e80c520c.js rel=prefetch><link href=js/chunk-e3edb682.604c708b.js rel=prefetch><link href=css/app.ba710877.css rel=preload as=style><link href=css/chunk-vendors.cb739fea.css rel=preload as=style><link href=js/app.89a6f443.js rel=preload as=script><link href=js/chunk-vendors.420bd1eb.js rel=preload as=script><link href=css/chunk-vendors.cb739fea.css rel=stylesheet><link href=css/app.ba710877.css rel=stylesheet></head><body><noscript><strong>We're sorry but m doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=js/chunk-vendors.420bd1eb.js></script><script src=js/app.89a6f443.js></script></body></html>
\ No newline at end of file
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-35027968"],{"4f37":function(t,e,s){"use strict";s("aa77")("trim",(function(t){return function(){return t(this,3)}}))},aa77:function(t,e,s){var n=s("5ca1"),a=s("be13"),o=s("79e5"),i=s("fdef"),r="["+i+"]",c="​…",l=RegExp("^"+r+r+"*"),u=RegExp(r+r+"*$"),m=function(t,e,s){var a={},r=o((function(){return!!i[t]()||c[t]()!=c})),l=a[t]=r?e(f):i[t];s&&(a[s]=l),n(n.P+n.F*r,"String",a)},f=m.trim=function(t,e){return t=String(a(t)),1&e&&(t=t.replace(l,"")),2&e&&(t=t.replace(u,"")),t};t.exports=m},bd01:function(t,e,s){"use strict";s.r(e);var n=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"login"},[s("div",{staticClass:"form"},[s("div",{staticClass:"left"},[s("div",{staticClass:"tit"},[t._v("欢迎回来, 请登录!")]),s("span",[t._v("客服系统-工作台")]),s("div",{staticClass:"client"},[s("p",[t._v("客户端下载")]),s("el-button",{attrs:{type:"primary",size:"mini",icon:"el-icon-download"},on:{click:t.dmac}},[t._v("Mac 版下载")]),s("el-button",{attrs:{type:"primary",size:"mini",icon:"el-icon-download"},on:{click:t.dwin}},[t._v("Windows 版下载")])],1)]),s("div",{staticClass:"right"},[s("el-form",{ref:"form",attrs:{model:t.form,onsubmit:"return false","label-width":"80px"}},[s("span",{staticClass:"lable"},[t._v("用户登录")]),s("el-input",{staticClass:"input",attrs:{placeholder:"请输入用户名","prefix-icon":"el-icon-user"},model:{value:t.form.username,callback:function(e){t.$set(t.form,"username",e)},expression:"form.username"}}),s("el-input",{staticClass:"input",attrs:{type:"password",placeholder:"请输入密码","prefix-icon":"el-icon-unlock","show-password":""},model:{value:t.form.password,callback:function(e){t.$set(t.form,"password",e)},expression:"form.password"}}),s("el-row",{staticClass:"btn-group",attrs:{type:"flex"}},[s("el-button",{attrs:{"native-type":"submit",size:"small",type:"primary"},on:{click:t.login}},[t._v("登录")]),s("el-button",{attrs:{size:"small",type:"info"}},[t._v("重置")])],1)],1)],1)])])},a=[],o=(s("4f37"),s("bc3a")),i=s.n(o),r={name:"login",data:function(){return{form:{username:"",password:""}}},mounted:function(){document.title="用户登录"},methods:{login:function(){var t=this;""!=this.form.username.trim()?""!=this.form.password.trim()?i.a.post("/auth/login",this.form).then((function(e){t.$store.commit("onChangeAdminInfo",e.data.data),t.$store.commit("onIsLogin",!0),localStorage.setItem("Authorization",e.data.data.token),t.$message({message:"登录成功!",type:"success"}),t.$router.push({path:"/index"})})).catch((function(e){console.log(e),t.$message.error(e.response.data.message)})):this.$message.error("密码不能为空!"):this.$message.error("用户名不能为空!")},dmac:function(){window.open("http://kf.aissz.com:666/static/app/mac-0.0.1.dmg")},dwin:function(){window.open("http://kf.aissz.com:666/static/app/win-0.0.1.exe")}}},c=r,l=(s("de49"),s("2877")),u=Object(l["a"])(c,n,a,!1,null,"27526a96",null);e["default"]=u.exports},de49:function(t,e,s){"use strict";var n=s("ed1b"),a=s.n(n);a.a},ed1b:function(t,e,s){},fdef:function(t,e){t.exports="\t\n\v\f\r   ᠎              \u2028\u2029\ufeff"}}]);
//# sourceMappingURL=chunk-35027968.79ffcf7b.js.map
\ No newline at end of file
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory(global):typeof define==="function"&&define.amd?define(factory):factory(global)})(typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:this,function(global){"use strict";global=global||{};var _Base64=global.Base64;var version="2.5.1";var buffer;if(typeof module!=="undefined"&&module.exports){try{buffer=eval("require('buffer').Buffer")}catch(err){buffer=undefined}}var b64chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var b64tab=function(bin){var t={};for(var i=0,l=bin.length;i<l;i++)t[bin.charAt(i)]=i;return t}(b64chars);var fromCharCode=String.fromCharCode;var cb_utob=function(c){if(c.length<2){var cc=c.charCodeAt(0);return cc<128?c:cc<2048?fromCharCode(192|cc>>>6)+fromCharCode(128|cc&63):fromCharCode(224|cc>>>12&15)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}else{var cc=65536+(c.charCodeAt(0)-55296)*1024+(c.charCodeAt(1)-56320);return fromCharCode(240|cc>>>18&7)+fromCharCode(128|cc>>>12&63)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}};var re_utob=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;var utob=function(u){return u.replace(re_utob,cb_utob)};var cb_encode=function(ccc){var padlen=[0,2,1][ccc.length%3],ord=ccc.charCodeAt(0)<<16|(ccc.length>1?ccc.charCodeAt(1):0)<<8|(ccc.length>2?ccc.charCodeAt(2):0),chars=[b64chars.charAt(ord>>>18),b64chars.charAt(ord>>>12&63),padlen>=2?"=":b64chars.charAt(ord>>>6&63),padlen>=1?"=":b64chars.charAt(ord&63)];return chars.join("")};var btoa=global.btoa?function(b){return global.btoa(b)}:function(b){return b.replace(/[\s\S]{1,3}/g,cb_encode)};var _encode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(u){return(u.constructor===buffer.constructor?u:buffer.from(u)).toString("base64")}:function(u){return(u.constructor===buffer.constructor?u:new buffer(u)).toString("base64")}:function(u){return btoa(utob(u))};var encode=function(u,urisafe){return!urisafe?_encode(String(u)):_encode(String(u)).replace(/[+\/]/g,function(m0){return m0=="+"?"-":"_"}).replace(/=/g,"")};var encodeURI=function(u){return encode(u,true)};var re_btou=new RegExp(["[À-ß][€-¿]","[à-ï][€-¿]{2}","[ð-÷][€-¿]{3}"].join("|"),"g");var cb_btou=function(cccc){switch(cccc.length){case 4:var cp=(7&cccc.charCodeAt(0))<<18|(63&cccc.charCodeAt(1))<<12|(63&cccc.charCodeAt(2))<<6|63&cccc.charCodeAt(3),offset=cp-65536;return fromCharCode((offset>>>10)+55296)+fromCharCode((offset&1023)+56320);case 3:return fromCharCode((15&cccc.charCodeAt(0))<<12|(63&cccc.charCodeAt(1))<<6|63&cccc.charCodeAt(2));default:return fromCharCode((31&cccc.charCodeAt(0))<<6|63&cccc.charCodeAt(1))}};var btou=function(b){return b.replace(re_btou,cb_btou)};var cb_decode=function(cccc){var len=cccc.length,padlen=len%4,n=(len>0?b64tab[cccc.charAt(0)]<<18:0)|(len>1?b64tab[cccc.charAt(1)]<<12:0)|(len>2?b64tab[cccc.charAt(2)]<<6:0)|(len>3?b64tab[cccc.charAt(3)]:0),chars=[fromCharCode(n>>>16),fromCharCode(n>>>8&255),fromCharCode(n&255)];chars.length-=[0,0,2,1][padlen];return chars.join("")};var _atob=global.atob?function(a){return global.atob(a)}:function(a){return a.replace(/\S{1,4}/g,cb_decode)};var atob=function(a){return _atob(String(a).replace(/[^A-Za-z0-9\+\/]/g,""))};var _decode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(a){return(a.constructor===buffer.constructor?a:buffer.from(a,"base64")).toString()}:function(a){return(a.constructor===buffer.constructor?a:new buffer(a,"base64")).toString()}:function(a){return btou(_atob(a))};var decode=function(a){return _decode(String(a).replace(/[-_]/g,function(m0){return m0=="-"?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))};var noConflict=function(){var Base64=global.Base64;global.Base64=_Base64;return Base64};global.Base64={VERSION:version,atob:atob,btoa:btoa,fromBase64:decode,toBase64:encode,utob:utob,encode:encode,encodeURI:encodeURI,btou:btou,decode:decode,noConflict:noConflict,__buffer__:buffer};if(typeof Object.defineProperty==="function"){var noEnum=function(v){return{value:v,enumerable:false,writable:true,configurable:true}};global.Base64.extendString=function(){Object.defineProperty(String.prototype,"fromBase64",noEnum(function(){return decode(this)}));Object.defineProperty(String.prototype,"toBase64",noEnum(function(urisafe){return encode(this,urisafe)}));Object.defineProperty(String.prototype,"toBase64URI",noEnum(function(){return encode(this,true)}))}}if(global["Meteor"]){Base64=global.Base64}if(typeof module!=="undefined"&&module.exports){module.exports.Base64=global.Base64}else if(typeof define==="function"&&define.amd){define([],function(){return global.Base64})}return{Base64:global.Base64}});
\ No newline at end of file
No preview for this file type
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment