feat: add custom list
This commit is contained in:
parent
a203de46f7
commit
eeda43d30c
16
cmd/root.go
16
cmd/root.go
@ -1,7 +1,10 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"ai-css/common"
|
||||
"ai-css/library/logger"
|
||||
"ai-css/models"
|
||||
"ai-css/ws"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@ -28,13 +31,16 @@ func args(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
func Execute() {
|
||||
logger.InitDefault()
|
||||
common.LoadConfig()
|
||||
rootCmd.AddCommand(serverCmd)
|
||||
rootCmd.AddCommand(installCmd)
|
||||
rootCmd.AddCommand(stopCmd)
|
||||
|
||||
models.Connect()
|
||||
ws.StartUpdateVisitorStatusCron()
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
func init() {
|
||||
rootCmd.AddCommand(serverCmd)
|
||||
rootCmd.AddCommand(installCmd)
|
||||
rootCmd.AddCommand(stopCmd)
|
||||
}
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
var (
|
||||
PageSize uint = 10
|
||||
VisitorPageSize uint = 8
|
||||
@ -10,3 +16,21 @@ var (
|
||||
MysqlConf string = Dir + "mysql.json"
|
||||
IsCompireTemplate bool = false //是否编译静态模板到二进制
|
||||
)
|
||||
|
||||
const (
|
||||
ENV_DEV = "dev"
|
||||
ENV_PROD = "prod"
|
||||
)
|
||||
|
||||
var (
|
||||
environment = os.Getenv("AICSS_ENV")
|
||||
)
|
||||
|
||||
func getConfigPath() string {
|
||||
switch environment {
|
||||
case ENV_DEV, ENV_PROD:
|
||||
return path.Join(Dir, fmt.Sprintf("config_%s.yaml", environment))
|
||||
default:
|
||||
return path.Join(Dir, "config.yaml")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +1,59 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"ai-css/tools"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Mysql struct {
|
||||
Server string
|
||||
Port string
|
||||
Database string
|
||||
Username string
|
||||
Password string
|
||||
type Config struct {
|
||||
Service Service `mapstructure:"service" json:"service"` // 服务配置
|
||||
MysqlService Mysql `mapstructure:"mysql_service" json:"mysql_service"`
|
||||
}
|
||||
|
||||
func GetMysqlConf() *Mysql {
|
||||
var mysql = &Mysql{}
|
||||
isExist, _ := tools.IsFileExist(MysqlConf)
|
||||
if !isExist {
|
||||
return mysql
|
||||
type Service struct {
|
||||
Sites map[string]SiteConfig `mapstructure:"site" json:"site"`
|
||||
}
|
||||
info, err := ioutil.ReadFile(MysqlConf)
|
||||
|
||||
type SiteConfig struct {
|
||||
UnauthURI string `mapstructure:"unauth_uri" json:"unauth_uri"`
|
||||
}
|
||||
|
||||
type Mysql struct {
|
||||
Server string `mapstructure:"server" json:"server"`
|
||||
Port string `mapstructure:"port" json:"port"`
|
||||
Database string `mapstructure:"database" json:"database"`
|
||||
Username string `mapstructure:"username" json:"username"`
|
||||
Password string `mapstructure:"password" json:"password"`
|
||||
}
|
||||
|
||||
var (
|
||||
cfg Config
|
||||
loadOnce sync.Once
|
||||
)
|
||||
|
||||
func LoadConfig() Config {
|
||||
loadOnce.Do(func() {
|
||||
var configPath = getConfigPath()
|
||||
viper.SetConfigType("yaml")
|
||||
viper.SetConfigFile(configPath)
|
||||
err := viper.ReadInConfig()
|
||||
if err != nil {
|
||||
return mysql
|
||||
panic(fmt.Errorf("read file failed err:%v,file:%s", err, configPath))
|
||||
}
|
||||
err = json.Unmarshal(info, mysql)
|
||||
return mysql
|
||||
err = viper.Unmarshal(&cfg)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("errpr viper.Unnarshal err:%v,file:%s", err, configPath))
|
||||
}
|
||||
})
|
||||
return cfg
|
||||
}
|
||||
|
||||
func GetMysqlConfig() Mysql {
|
||||
return cfg.MysqlService
|
||||
}
|
||||
|
||||
func GetServiceConfig() Service {
|
||||
return cfg.Service
|
||||
}
|
||||
|
||||
6
config/config.yaml
Normal file
6
config/config.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
mysql_service:
|
||||
server: xpink-prod-mysql.cjkmm024cf76.ap-east-1.rds.amazonaws.com
|
||||
port: 3306
|
||||
database: aicss_db
|
||||
username: admin
|
||||
password: o()Vq$NuZdwoEe>VLalG]CBMp4LZ
|
||||
6
config/config_prod.yaml
Normal file
6
config/config_prod.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
mysql_service:
|
||||
server: xpink-prod-mysql.cjkmm024cf76.ap-east-1.rds.amazonaws.com
|
||||
port: 3306
|
||||
database: aicss_db
|
||||
username: admin
|
||||
password: o()Vq$NuZdwoEe>VLalG]CBMp4LZ
|
||||
@ -5,10 +5,65 @@ import (
|
||||
"ai-css/tools"
|
||||
"ai-css/ws"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func PostKefuStatus(c *gin.Context) {
|
||||
v := c.GetString("kefu_name")
|
||||
userInfo := models.FindUser(v)
|
||||
if userInfo.Role != 1 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "权限不足",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
kefuName := c.PostForm("username")
|
||||
statusStr := c.PostForm("status")
|
||||
status, _ := strconv.Atoi(statusStr)
|
||||
|
||||
models.UpdateUserStatus(kefuName, int32(status))
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"result": "",
|
||||
})
|
||||
}
|
||||
|
||||
func PostAdminResetPass(c *gin.Context) {
|
||||
v := c.GetString("kefu_name")
|
||||
userInfo := models.FindUser(v)
|
||||
if userInfo.Role != 1 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "权限不足",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
kefuName := c.PostForm("username")
|
||||
newPass := c.PostForm("password")
|
||||
|
||||
if kefuName == "" || newPass == "" {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "Username and password are required",
|
||||
"result": "",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
models.UpdateUserPass(kefuName, tools.Md5(newPass))
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"result": "",
|
||||
})
|
||||
}
|
||||
|
||||
func PostKefuAvator(c *gin.Context) {
|
||||
|
||||
avator := c.PostForm("avator")
|
||||
@ -78,23 +133,47 @@ func PostKefuClient(c *gin.Context) {
|
||||
|
||||
func GetIdleKefu(c *gin.Context) {
|
||||
visitorId := c.Query("visitor_id")
|
||||
var kefuName string
|
||||
var kefuName, oldKefuname string
|
||||
var reset bool
|
||||
var visitorDataId uint
|
||||
if visitorId != "" {
|
||||
visitor := models.FindVisitorByVistorId(visitorId)
|
||||
if visitor.ToId != "" {
|
||||
kefuName = visitor.ToId
|
||||
visitorDataId = visitor.ID
|
||||
}
|
||||
if kefuName != "" {
|
||||
userInfo := models.FindUser(kefuName)
|
||||
if userInfo.ID == 0 || userInfo.Status == 0 || userInfo.IsOnline == 0 {
|
||||
reset = true
|
||||
oldKefuname = kefuName
|
||||
kefuName = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
if kefuName == "" {
|
||||
user := models.FindIdleUser()
|
||||
kefuName = user.Name
|
||||
if reset {
|
||||
if visitorDataId > 0 {
|
||||
models.UpdatesVisitor(visitorDataId, kefuName)
|
||||
}
|
||||
}
|
||||
}
|
||||
if kefuName == "" {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "暂时没有在线客服",
|
||||
})
|
||||
} else {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"result": kefuName,
|
||||
"oldResult": oldKefuname,
|
||||
})
|
||||
}
|
||||
}
|
||||
func GetKefuInfo(c *gin.Context) {
|
||||
kefuName, _ := c.Get("kefu_name")
|
||||
user := models.FindUser(kefuName.(string))
|
||||
@ -124,7 +203,7 @@ func GetOtherKefuList(c *gin.Context) {
|
||||
id := idStr.(float64)
|
||||
result := make([]interface{}, 0)
|
||||
ws.SendPingToKefuClient()
|
||||
kefus := models.FindUsers()
|
||||
kefus := models.FindUsers("")
|
||||
for _, kefu := range kefus {
|
||||
if uint(id) == kefu.ID {
|
||||
continue
|
||||
@ -180,6 +259,15 @@ func GetKefuInfoSetting(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
func PostKefuRegister(c *gin.Context) {
|
||||
kefuName := c.GetString("kefu_name")
|
||||
userInfo := models.FindUser(kefuName)
|
||||
if userInfo.Role != 1 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "权限不足",
|
||||
})
|
||||
return
|
||||
}
|
||||
name := c.PostForm("username")
|
||||
password := c.PostForm("password")
|
||||
nickname := c.PostForm("nickname")
|
||||
@ -246,7 +334,16 @@ func PostKefuInfo(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
func GetKefuList(c *gin.Context) {
|
||||
users := models.FindUsers()
|
||||
kefuName := c.GetString("kefu_name")
|
||||
userInfo := models.FindUser(kefuName)
|
||||
if userInfo.Role != 1 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "权限不足",
|
||||
})
|
||||
return
|
||||
}
|
||||
users := models.FindUsers(kefuName)
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "获取成功",
|
||||
|
||||
@ -3,8 +3,9 @@ package controller
|
||||
import (
|
||||
"ai-css/models"
|
||||
"ai-css/tools"
|
||||
"github.com/gin-gonic/gin"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Summary User Authentication API
|
||||
@ -33,6 +34,14 @@ func LoginCheckPass(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if info.Status != 1 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"message": "账号已经停用",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Prepare user session data
|
||||
userinfo := map[string]interface{}{
|
||||
"kefu_name": info.Name,
|
||||
|
||||
@ -16,6 +16,7 @@ func JwtPageMiddleware(c *gin.Context) {
|
||||
// c.Abort()
|
||||
//}
|
||||
}
|
||||
|
||||
func JwtApiMiddleware(c *gin.Context) {
|
||||
token := c.GetHeader("aicss-token")
|
||||
if token == "" {
|
||||
|
||||
31
middleware/login_direct/login.go
Normal file
31
middleware/login_direct/login.go
Normal file
@ -0,0 +1,31 @@
|
||||
package login_direct
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func DomainAuthRedirectMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
host := c.Request.Host
|
||||
|
||||
// 只针对 admin 域名
|
||||
if strings.HasPrefix(host, "admin.example.com") {
|
||||
|
||||
// 这里写你的认证逻辑
|
||||
token := c.GetHeader("Authorization")
|
||||
|
||||
if token == "" {
|
||||
// 未认证跳转
|
||||
c.Redirect(http.StatusFound, "https://admin.example.com/login")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@ -18,25 +18,21 @@ type Model struct {
|
||||
DeletedAt *time.Time `sql:"index" json:"deleted_at"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
Connect()
|
||||
}
|
||||
func Connect() error {
|
||||
mysql := common.GetMysqlConf()
|
||||
func Connect() {
|
||||
mysql := common.GetMysqlConfig()
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", mysql.Username, mysql.Password, mysql.Server, mysql.Port, mysql.Database)
|
||||
var err error
|
||||
DB, err = gorm.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
panic("数据库连接失败!")
|
||||
return err
|
||||
}
|
||||
DB.SingularTable(true)
|
||||
DB.LogMode(true)
|
||||
DB.DB().SetMaxIdleConns(10)
|
||||
DB.DB().SetMaxOpenConns(100)
|
||||
DB.DB().SetConnMaxLifetime(59 * time.Second)
|
||||
return nil
|
||||
DB.AutoMigrate(&User{})
|
||||
}
|
||||
func Execute(sql string) error {
|
||||
return DB.Exec(sql).Error
|
||||
|
||||
@ -16,6 +16,8 @@ type User struct {
|
||||
Role int32 `json:"role"`
|
||||
RoleName string `json:"role_name" sql:"-"`
|
||||
RoleId string `json:"role_id" sql:"-"`
|
||||
Status int32 `json:"status" gorm:"default:1"` // 1: active, 0: inactive
|
||||
IsOnline int32 `json:"is_online" gorm:"default:0"` // 1: online, 0: offline
|
||||
}
|
||||
|
||||
func CreateUser(name string, password string, avator string, nickname string) uint {
|
||||
@ -29,6 +31,15 @@ func CreateUser(name string, password string, avator string, nickname string) ui
|
||||
DB.Create(user)
|
||||
return user.ID
|
||||
}
|
||||
|
||||
func UpdateUserIsOnline(name string, isOnline int32) {
|
||||
user := &User{
|
||||
IsOnline: isOnline,
|
||||
}
|
||||
user.UpdatedAt = time.Now()
|
||||
DB.Model(user).Where("name = ?", name).Update("IsOnline", isOnline)
|
||||
}
|
||||
|
||||
func UpdateUser(name string, password string, avator string, nickname string) {
|
||||
user := &User{
|
||||
Avator: avator,
|
||||
@ -47,6 +58,13 @@ func UpdateUserPass(name string, pass string) {
|
||||
user.UpdatedAt = time.Now()
|
||||
DB.Model(user).Where("name = ?", name).Update("Password", pass)
|
||||
}
|
||||
func UpdateUserStatus(name string, status int32) {
|
||||
user := &User{
|
||||
Status: status,
|
||||
}
|
||||
user.UpdatedAt = time.Now()
|
||||
DB.Model(user).Where("name = ?", name).Update("Status", status)
|
||||
}
|
||||
func UpdateUserAvator(name string, avator string) {
|
||||
user := &User{
|
||||
Avator: avator,
|
||||
@ -70,7 +88,7 @@ func FindIdleUser() User {
|
||||
defer assignMutex.Unlock()
|
||||
|
||||
var users []User
|
||||
DB.Where("name != ?", "admin").Order("id desc").Find(&users)
|
||||
DB.Where("name != ? and `status` = 1 and `is_online` = 1", "admin").Order("id desc").Find(&users)
|
||||
|
||||
if len(users) == 0 {
|
||||
return User{}
|
||||
@ -104,9 +122,13 @@ func FindUserById(id interface{}) User {
|
||||
func DeleteUserById(id string) {
|
||||
DB.Where("id = ?", id).Delete(User{})
|
||||
}
|
||||
func FindUsers() []User {
|
||||
func FindUsers(withoutUsername string) []User {
|
||||
var users []User
|
||||
DB.Select("user.*,role.name role_name").Joins("left join user_role on user.id=user_role.user_id").Joins("left join role on user_role.role_id=role.id").Order("user.id desc").Find(&users)
|
||||
if withoutUsername == "" {
|
||||
DB.Find(&users)
|
||||
} else {
|
||||
DB.Where("name != ?", withoutUsername).Find(&users)
|
||||
}
|
||||
return users
|
||||
}
|
||||
func FindUserRole(query interface{}, id interface{}) User {
|
||||
|
||||
@ -39,6 +39,11 @@ func FindVisitorByVistorId(visitorId string) Visitor {
|
||||
DB.Where("visitor_id = ?", visitorId).First(&v)
|
||||
return v
|
||||
}
|
||||
|
||||
func UpdatesVisitor(id uint, toId string) {
|
||||
DB.Model(&Visitor{}).Where("id = ?", id).Update("to_id", toId)
|
||||
}
|
||||
|
||||
func FindVisitors(page uint, pagesize uint) []Visitor {
|
||||
offset := (page - 1) * pagesize
|
||||
if offset < 0 {
|
||||
|
||||
Binary file not shown.
@ -25,7 +25,7 @@ func InitApiRouter(engine *gin.RouterGroup) {
|
||||
engine.POST("/check", controller.LoginCheckPass)
|
||||
|
||||
engine.GET("/userinfo", middleware.JwtApiMiddleware, controller.GetKefuInfoAll)
|
||||
engine.POST("/register", middleware.Ipblack, controller.PostKefuRegister)
|
||||
engine.POST("/register", middleware.JwtApiMiddleware, controller.PostKefuRegister)
|
||||
engine.POST("/install", controller.PostInstall)
|
||||
//前后聊天
|
||||
engine.GET("/ws_kefu", middleware.JwtApiMiddleware, ws.NewKefuServer)
|
||||
@ -50,6 +50,8 @@ func InitApiRouter(engine *gin.RouterGroup) {
|
||||
engine.POST("/kefuinfo", middleware.JwtApiMiddleware, middleware.RbacAuth, controller.PostKefuInfo)
|
||||
engine.DELETE("/kefuinfo", middleware.JwtApiMiddleware, middleware.RbacAuth, controller.DeleteKefuInfo)
|
||||
engine.GET("/kefulist", middleware.JwtApiMiddleware, middleware.RbacAuth, controller.GetKefuList)
|
||||
engine.POST("/kefu_status", middleware.JwtApiMiddleware, middleware.RbacAuth, controller.PostKefuStatus)
|
||||
engine.POST("/admin_reset_pass", middleware.JwtApiMiddleware, middleware.RbacAuth, controller.PostAdminResetPass)
|
||||
engine.GET("/other_kefulist", middleware.JwtApiMiddleware, controller.GetOtherKefuList)
|
||||
engine.GET("/trans_kefu", middleware.JwtApiMiddleware, controller.PostTransKefu)
|
||||
engine.POST("/modifypass", middleware.JwtApiMiddleware, middleware.RbacAuth, controller.PostKefuPass)
|
||||
|
||||
@ -726,6 +726,16 @@
|
||||
success: function(data) {
|
||||
if(data.code==200 && data.msg=="ok"){
|
||||
KEFU_ID=data.result;
|
||||
var oldResult = data.oldResult;
|
||||
if (oldResult && oldResult != KEFU_ID) {
|
||||
var oldKey = "visitor_" + oldResult;
|
||||
var newKey = "visitor_" + KEFU_ID;
|
||||
var oldCache = localStorage.getItem(oldKey);
|
||||
if(oldCache) {
|
||||
localStorage.setItem(newKey, oldCache);
|
||||
localStorage.removeItem(oldKey);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
KEFU_ID="default";
|
||||
}
|
||||
|
||||
@ -121,6 +121,8 @@
|
||||
]
|
||||
},
|
||||
showRegHtml: false,
|
||||
kefuInfo:{},
|
||||
kefuList: [],
|
||||
},
|
||||
methods: {
|
||||
validatePasswordMatch(rule, value, callback) {
|
||||
@ -190,7 +192,16 @@
|
||||
"nickname": this.form.nickname,
|
||||
};
|
||||
|
||||
$.post("/aicss/register", data, (response) => {
|
||||
$.ajax({
|
||||
url: "/aicss/register",
|
||||
type: "POST",
|
||||
data: data,
|
||||
headers: {
|
||||
"aicss-token": localStorage.getItem("aicss-token"),
|
||||
"X-Client-Type": "web",
|
||||
"X-Custom-Domain": window.location.host
|
||||
},
|
||||
success: (response) => {
|
||||
if (response.code === 200) {
|
||||
this.$message({
|
||||
message: 'Account created successfully!',
|
||||
@ -203,18 +214,23 @@
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}).fail(() => {
|
||||
},
|
||||
error: () => {
|
||||
this.$message({
|
||||
message: 'Connection error',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
if (top.location != location) {
|
||||
top.location.href = location.href;
|
||||
}
|
||||
|
||||
this.getKefuInfo();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -115,6 +115,43 @@
|
||||
</div>
|
||||
>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="kefuInfo.role === 1" label="Customer List">
|
||||
<div class="profile-form">
|
||||
<div style="margin-bottom: 20px;">
|
||||
<el-button type="primary" icon="el-icon-plus" @click="openCreateKefu">添加客服</el-button>
|
||||
</div>
|
||||
<el-table :data="kefuList" style="width: 100%" stripe>
|
||||
<el-table-column label="头像" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-avatar :size="40" :src="scope.row.avator"></el-avatar>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="账号"></el-table-column>
|
||||
<el-table-column prop="nickname" label="昵称"></el-table-column>
|
||||
<el-table-column label="在线状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.is_online === 1" type="success" size="mini">在线</el-tag>
|
||||
<el-tag v-else type="info" size="mini">离线</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
@change="handleStatusChange(scope.row)">
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="warning" @click="openResetPass(scope.row)">重置密码</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="kefuInfo.role === 1" label="System Configuration">
|
||||
<div class="profile-form" style="margin-top: 20px">
|
||||
<el-table
|
||||
@ -174,6 +211,36 @@
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-dialog title="创建客服" :visible.sync="createKefuDialog" width="400px">
|
||||
<el-form :model="createKefuForm">
|
||||
<el-form-item label="账号">
|
||||
<el-input v-model="createKefuForm.username"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称">
|
||||
<el-input v-model="createKefuForm.nickname"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input type="password" v-model="createKefuForm.password" show-password></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="createKefuDialog = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitCreateKefu">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="重置密码" :visible.sync="resetPassDialog" width="400px">
|
||||
<el-form :model="resetPassForm">
|
||||
<el-form-item label="新密码">
|
||||
<el-input type="password" v-model="resetPassForm.password" show-password></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="resetPassDialog = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitResetPass">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@ -25,6 +25,18 @@
|
||||
role_name:"",
|
||||
role_id:"",
|
||||
},
|
||||
kefuList: [],
|
||||
resetPassDialog: false,
|
||||
createKefuDialog: false,
|
||||
resetPassForm: {
|
||||
username: "",
|
||||
password: ""
|
||||
},
|
||||
createKefuForm: {
|
||||
username: "",
|
||||
nickname: "",
|
||||
password: ""
|
||||
},
|
||||
|
||||
avatarUrl:"",
|
||||
chatEndpoint: "",
|
||||
@ -303,8 +315,71 @@ A: 因二次结算导致,系统将按最终结果扣回或补发。`}
|
||||
if(data.code==200 && data.result!=null){
|
||||
_this.kefuInfo=data.result;
|
||||
_this.chatEndpoint=window.location.origin + '/aicss/livechat?kefu_id='+data.result.username;
|
||||
if (_this.kefuInfo.role === 1) {
|
||||
_this.getKefuList();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// Get Kefu List
|
||||
getKefuList() {
|
||||
let _this = this;
|
||||
this.sendAjax("/aicss/kefulist", "get", {}, function(result) {
|
||||
_this.kefuList = result;
|
||||
});
|
||||
},
|
||||
// Toggle Status
|
||||
handleStatusChange(row) {
|
||||
let _this = this;
|
||||
this.sendAjax("/kefu_status", "post", {
|
||||
username: row.name,
|
||||
status: row.status
|
||||
}, function(result) {
|
||||
_this.$message({
|
||||
message: "Status updated",
|
||||
type: 'success'
|
||||
});
|
||||
});
|
||||
},
|
||||
// Open Reset Password Dialog
|
||||
openResetPass(row) {
|
||||
this.resetPassForm.username = row.name;
|
||||
this.resetPassForm.password = "";
|
||||
this.resetPassDialog = true;
|
||||
},
|
||||
// Submit Reset Password
|
||||
submitResetPass() {
|
||||
let _this = this;
|
||||
if (this.resetPassForm.password.length < 2) {
|
||||
this.$message.error("Password too short");
|
||||
return;
|
||||
}
|
||||
this.sendAjax("/admin_reset_pass", "post", this.resetPassForm, function(result) {
|
||||
_this.$message.success("Password reset successfully");
|
||||
_this.resetPassDialog = false;
|
||||
});
|
||||
},
|
||||
// Open Create User Dialog
|
||||
openCreateKefu() {
|
||||
this.createKefuForm = {
|
||||
username: "",
|
||||
nickname: "",
|
||||
password: ""
|
||||
};
|
||||
this.createKefuDialog = true;
|
||||
},
|
||||
// Submit Create User
|
||||
submitCreateKefu() {
|
||||
let _this = this;
|
||||
if (!this.createKefuForm.username || !this.createKefuForm.password) {
|
||||
this.$message.error("Username and password required");
|
||||
return;
|
||||
}
|
||||
this.sendAjax("/register", "post", this.createKefuForm, function(result) {
|
||||
_this.$message.success("User created successfully");
|
||||
_this.createKefuDialog = false;
|
||||
_this.getKefuList();
|
||||
});
|
||||
},
|
||||
},
|
||||
@ -325,4 +400,5 @@ A: 因二次结算导致,系统将按最终结果扣回或补发。`}
|
||||
|
||||
</script>
|
||||
</html>
|
||||
|
||||
{{end}}
|
||||
@ -1,16 +1,13 @@
|
||||
package tmpl
|
||||
|
||||
import (
|
||||
"ai-css/tools"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 登陆界面
|
||||
func PageLogin(c *gin.Context) {
|
||||
if noExist, _ := tools.IsFileNotExist("./install.lock"); noExist {
|
||||
c.Redirect(302, "/install")
|
||||
}
|
||||
c.HTML(http.StatusOK, "login.html", nil)
|
||||
}
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ func NewKefuServer(c *gin.Context) {
|
||||
kefu.Avator = kefuInfo.Avator
|
||||
kefu.Conn = conn
|
||||
AddKefuToList(&kefu)
|
||||
models.UpdateUserIsOnline(kefuInfo.Name, 1)
|
||||
|
||||
for {
|
||||
//接受消息
|
||||
@ -43,6 +44,7 @@ func NewKefuServer(c *gin.Context) {
|
||||
if err != nil {
|
||||
log.Println("ws/user.go ", err)
|
||||
conn.Close()
|
||||
models.UpdateUserIsOnline(kefuInfo.Name, 0)
|
||||
return
|
||||
}
|
||||
|
||||
@ -114,6 +116,7 @@ func SendPingToKefuClient() {
|
||||
if err != nil {
|
||||
log.Println("定时发送ping给客服,失败", err.Error())
|
||||
delete(KefuList, kefuId)
|
||||
models.UpdateUserIsOnline(kefuId, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
ws/ws.go
3
ws/ws.go
@ -64,7 +64,7 @@ var message = make(chan *Message, 10)
|
||||
var upgrader = websocket.Upgrader{}
|
||||
var Mux sync.RWMutex
|
||||
|
||||
func init() {
|
||||
func StartUpdateVisitorStatusCron() {
|
||||
upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
@ -75,6 +75,7 @@ func init() {
|
||||
}
|
||||
go UpdateVisitorStatusCron()
|
||||
}
|
||||
|
||||
func SendServerJiang(title string, content string, domain string) string {
|
||||
noticeServerJiang, err := strconv.ParseBool(models.FindConfig("NoticeServerJiang"))
|
||||
serverJiangAPI := models.FindConfig("ServerJiangAPI")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user