diff --git a/app/md/app_redis_key.go b/app/cfg/app_redis_key.go similarity index 81% rename from app/md/app_redis_key.go rename to app/cfg/app_redis_key.go index 08fb5bb..5de3886 100644 --- a/app/md/app_redis_key.go +++ b/app/cfg/app_redis_key.go @@ -1,4 +1,4 @@ -package md +package cfg // 缓存key统一管理, %s格式化为masterId const ( @@ -14,3 +14,7 @@ const ( AdminRolePermissionCacheTime = 3600 * 24 * 0.5 ) + +const ( + IM_KEY_SYS_CFG_CACHE = "egg_im_sys_cfg_cache" //im配置表缓存键 +) diff --git a/app/cfg/cfg_app.go b/app/cfg/cfg_app.go index a0088d2..ba2dbe0 100644 --- a/app/cfg/cfg_app.go +++ b/app/cfg/cfg_app.go @@ -12,6 +12,7 @@ type Config struct { RedisAddr string `yaml:"redis_addr"` RedisPassword string `yaml:"redis_password"` DB DBCfg `yaml:"db"` + ImDB DBCfg `yaml:"im_db"` Log LogCfg `yaml:"log"` ImBusinessRpc ImBusinessRpcCfg `yaml:"im_business_rpc"` ImLogicRpc ImLogicRpcCfg `yaml:"im_logic_rpc"` diff --git a/app/cfg/cfg_cache_key.go b/app/cfg/cfg_cache_key.go deleted file mode 100644 index c091909..0000000 --- a/app/cfg/cfg_cache_key.go +++ /dev/null @@ -1,3 +0,0 @@ -package cfg - -// 统一管理缓存 diff --git a/app/cfg/init_cfg.go b/app/cfg/init_cfg.go index e6e11c7..384c36b 100644 --- a/app/cfg/init_cfg.go +++ b/app/cfg/init_cfg.go @@ -16,6 +16,7 @@ var ( RedisAddr string RedisPassword string DB *DBCfg + IMDB *DBCfg MQ *MQCfg ES *ESCfg Log *LogCfg @@ -44,6 +45,7 @@ func InitCfg() { Prd = conf.Prd Debug = conf.Debug DB = &conf.DB + IMDB = &conf.ImDB Log = &conf.Log RedisAddr = conf.RedisAddr RedisPassword = conf.RedisPassword diff --git a/app/cfg/init_task.go b/app/cfg/init_task.go index 0eec20e..2a049fd 100644 --- a/app/cfg/init_task.go +++ b/app/cfg/init_task.go @@ -27,6 +27,7 @@ func InitTaskCfg() { Prd = conf.Prd Debug = conf.Debug DB = &conf.DB + IMDB = &conf.ImDB Log = &conf.Log RedisAddr = conf.RedisAddr } diff --git a/app/db/db.go b/app/db/db.go index 00630e8..223445e 100644 --- a/app/db/db.go +++ b/app/db/db.go @@ -16,7 +16,7 @@ import ( var Db *xorm.Engine -//根据DB配置文件初始化数据库 +// 根据DB配置文件初始化数据库 func InitDB(c *cfg.DBCfg) error { var ( err error @@ -51,6 +51,43 @@ func InitDB(c *cfg.DBCfg) error { return nil } +var DbIm *xorm.Engine + +// 根据DB配置文件初始化数据库 +func InitImDB(c *cfg.DBCfg) error { + var ( + err error + f *os.File + ) + //创建Orm引擎 + if DbIm, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4", c.User, c.Psw, c.Host, c.Name)); err != nil { + return err + } + DbIm.SetConnMaxLifetime(c.MaxLifetime * time.Second) //设置最长连接时间 + DbIm.SetMaxOpenConns(c.MaxOpenConns) //设置最大打开连接数 + DbIm.SetMaxIdleConns(c.MaxIdleConns) //设置连接池的空闲数大小 + if err = DbIm.Ping(); err != nil { //尝试ping数据库 + return err + } + if c.ShowLog { //根据配置文件设置日志 + DbIm.ShowSQL(true) //设置是否打印sql + DbIm.Logger().SetLevel(0) //设置日志等级 + //修改日志文件存放路径文件名是%s.log + path := fmt.Sprintf(c.Path, c.Name) + f, err = os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777) + if err != nil { + os.RemoveAll(c.Path) + if f, err = os.OpenFile(c.Path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777); err != nil { + return err + } + } + logger := log.NewSimpleLogger(f) + logger.ShowSQL(true) + DbIm.SetLogger(logger) + } + return nil +} + /********************************************* 公用方法 *********************************************/ // 数据批量插入 diff --git a/app/db/db_sys_cfg.go b/app/db/db_sys_cfg.go index 6f49814..75fd914 100644 --- a/app/db/db_sys_cfg.go +++ b/app/db/db_sys_cfg.go @@ -2,10 +2,7 @@ package db import ( "applet/app/db/model" - "applet/app/md" - "applet/app/utils/cache" "applet/app/utils/logx" - "fmt" "xorm.io/xorm" ) @@ -47,36 +44,3 @@ func SysCfgUpdate(Db *xorm.Engine, key, val, memo string) bool { } return true } -func SysCfgGetWithDb(eg *xorm.Engine, masterId string, HKey string) string { - cacheKey := fmt.Sprintf(md.AppCfgCacheKey, masterId) + HKey - get, err := cache.GetString(cacheKey) - if err != nil || get == "" { - cfg, err := SysCfgGetOne(eg, HKey) - if err != nil || cfg == nil { - _ = logx.Error(err) - return "" - } - - // key是否存在 - cacheKeyExist := false - if cache.Exists(cacheKey) { - cacheKeyExist = true - } - - // 设置缓存 - _, err = cache.SetEx(cacheKey, cfg.Val, 30) - if err != nil { - _ = logx.Error(err) - return "" - } - if !cacheKeyExist { // 如果是首次设置 设置过期时间 - _, err := cache.Expire(cacheKey, md.CfgCacheTime) - if err != nil { - _ = logx.Error(err) - return "" - } - } - return cfg.Val - } - return get -} diff --git a/app/db/im/db_customer_service.go b/app/db/im/db_customer_service.go new file mode 100644 index 0000000..3a49c63 --- /dev/null +++ b/app/db/im/db_customer_service.go @@ -0,0 +1 @@ +package db diff --git a/app/db/im/db_sys_cfg.go b/app/db/im/db_sys_cfg.go new file mode 100644 index 0000000..75fd914 --- /dev/null +++ b/app/db/im/db_sys_cfg.go @@ -0,0 +1,46 @@ +package db + +import ( + "applet/app/db/model" + "applet/app/utils/logx" + "xorm.io/xorm" +) + +// 系统配置get +func SysCfgGetAll(Db *xorm.Engine) (*[]model.SysCfg, error) { + var cfgList []model.SysCfg + if err := Db.Cols("key,val,memo").Find(&cfgList); err != nil { + return nil, logx.Error(err) + } + return &cfgList, nil +} + +// 获取一条记录 +func SysCfgGetOne(Db *xorm.Engine, key string) (*model.SysCfg, error) { + var cfgList model.SysCfg + if has, err := Db.Where("`key`=?", key).Get(&cfgList); err != nil || has == false { + return nil, logx.Error(err) + } + return &cfgList, nil +} + +// 返回最后插入id +func SysCfgInsert(Db *xorm.Engine, key, val, memo string) bool { + cfg := model.SysCfg{Key: key, Val: val, Memo: memo} + _, err := Db.InsertOne(&cfg) + if err != nil { + logx.Error(err) + return false + } + return true +} + +func SysCfgUpdate(Db *xorm.Engine, key, val, memo string) bool { + cfg := model.SysCfg{Key: key, Val: val, Memo: memo} + _, err := Db.Where("`key`=?", key).Cols("val,memo").Update(&cfg) + if err != nil { + logx.Error(err) + return false + } + return true +} diff --git a/app/db/im/model/customer_service.go b/app/db/im/model/customer_service.go new file mode 100644 index 0000000..120319f --- /dev/null +++ b/app/db/im/model/customer_service.go @@ -0,0 +1,15 @@ +package model + +import "time" + +// CustomerService 客服表 +type CustomerService struct { + Id int64 + Uid int64 // 用户id + Weight int32 // 权重 + State int32 // 状态 + HasUserNums int // 拥有用户数量 + Memo string // 备注 + CreateTime time.Time // 创建时间 + UpdateTime time.Time // 更新时间 +} diff --git a/app/db/im/model/emoticon.go b/app/db/im/model/emoticon.go new file mode 100644 index 0000000..ac310a5 --- /dev/null +++ b/app/db/im/model/emoticon.go @@ -0,0 +1,12 @@ +package model + +type Emoticon struct { + Id int64 + Name string // 名称 + ImgUrl string // 图片地址 + Memo string // 备注 + Sort int // 排序 + State int // 状态0关闭,1开启 + CreateAt string // 创建时间 + UpdateAt string // 更新时间 +} diff --git a/app/db/im/model/sys_cfg.go b/app/db/im/model/sys_cfg.go new file mode 100644 index 0000000..22d906b --- /dev/null +++ b/app/db/im/model/sys_cfg.go @@ -0,0 +1,7 @@ +package model + +type SysCfg struct { + Key string `json:"key" xorm:"not null pk comment('键') VARCHAR(127)"` + Val string `json:"val" xorm:"comment('值') TEXT"` + Memo string `json:"memo" xorm:"not null default '' comment('备注') VARCHAR(255)"` +} diff --git a/app/db/im/model/user.go b/app/db/im/model/user.go new file mode 100644 index 0000000..cc601e3 --- /dev/null +++ b/app/db/im/model/user.go @@ -0,0 +1,18 @@ +package model + +import ( + "time" +) + +// User 账户 +type User struct { + Id int64 // 用户id + PhoneNumber string // 手机号 + Nickname string // 昵称 + Sex int32 // 性别,1:男;2:女 + AvatarUrl string // 用户头像 + Extra string // 附加属性 + CreateTime time.Time // 创建时间 + UpdateTime time.Time // 更新时间 + IsAutoAddedFriends int // 是否自动被添加好友 +} diff --git a/app/enum/im/enum_sys_cfg.go b/app/enum/im/enum_sys_cfg.go new file mode 100644 index 0000000..f5b8295 --- /dev/null +++ b/app/enum/im/enum_sys_cfg.go @@ -0,0 +1,31 @@ +package enum + +type SysCfg string + +const ( + IsAllowAddFriends = "is_allow_add_friends" + IsAutoAddFriends = "is_auto_add_friends" + ProhibitAddGroupMembers = "prohibit_add_group_members" + ProhibitRemoveGroupMembers = "prohibit_remove_group_members" + ProhibitUpdateGroupName = "prohibit_update_group_name" + ChatSensitiveWords = "chat_sensitive_words" +) + +func (gt SysCfg) String() string { + switch gt { + case IsAllowAddFriends: + return "是否允许加好友" + case IsAutoAddFriends: + return "是否自动加好友" + case ProhibitAddGroupMembers: + return "是否禁止添加群成员" + case ProhibitRemoveGroupMembers: + return "是否禁止移出群成员" + case ProhibitUpdateGroupName: + return "是否禁止修改群名称" + case ChatSensitiveWords: + return "聊天敏感词" + default: + return "未知" + } +} diff --git a/app/hdl/hdl_login.go b/app/hdl/hdl_login.go index 57fa13b..cff2b99 100644 --- a/app/hdl/hdl_login.go +++ b/app/hdl/hdl_login.go @@ -1,6 +1,7 @@ package hdl import ( + "applet/app/cfg" "applet/app/db" "applet/app/e" "applet/app/lib/validate" @@ -46,7 +47,7 @@ func Login(c *gin.Context) { return } ip := utils.GetIP(c.Request) - key := fmt.Sprintf(md.JwtTokenKey, ip, utils.AnyToString(admin.AdmId)) + key := fmt.Sprintf(cfg.JwtTokenKey, ip, utils.AnyToString(admin.AdmId)) token, err := svc.HandleLoginToken(key, admin) if err != nil { e.OutErr(c, e.ERR, err.Error()) diff --git a/app/hdl/im/hdl_basic.go b/app/hdl/im/hdl_basic.go new file mode 100644 index 0000000..e0cda63 --- /dev/null +++ b/app/hdl/im/hdl_basic.go @@ -0,0 +1,85 @@ +package im + +import ( + "applet/app/e" + enum "applet/app/enum/im" + md "applet/app/md/im" + svc "applet/app/svc/im" + "github.com/gin-gonic/gin" +) + +// GetBasic +// @Summary Im-基础设置(获取) +// @Tags 基础设置 +// @Description 基础设置(获取) +// @Accept json +// @Produce json +// @param Authorization header string true "验证参数Bearer和token空格拼接" +// @Success 200 {object} md.GetBasicResp "设置列表" +// @Failure 400 {object} md.Response "具体错误" +// @Router /api/im/getBasic [get] +func GetBasic(c *gin.Context) { + res := svc.SysCfgFindComm(enum.IsAllowAddFriends, enum.IsAutoAddFriends, enum.ProhibitAddGroupMembers, enum.ProhibitRemoveGroupMembers, enum.ProhibitUpdateGroupName, enum.ChatSensitiveWords) + + resp := md.GetBasicResp{ + IsAllowAddFriends: res[enum.IsAllowAddFriends], + IsAutoAddFriends: res[enum.IsAutoAddFriends], + ProhibitAddGroupMembers: res[enum.ProhibitAddGroupMembers], + ProhibitRemoveGroupMembers: res[enum.ProhibitRemoveGroupMembers], + ProhibitUpdateGroupName: res[enum.ProhibitUpdateGroupName], + ChatSensitiveWords: res[enum.ProhibitUpdateGroupName], + } + + e.OutSuc(c, resp, nil) +} + +// SetBasic +// @Summary Im-基础设置(更新) +// @Tags 基础设置 +// @Description 基础设置(更新) +// @Accept json +// @Produce json +// @param Authorization header string true "验证参数Bearer和token空格拼接" +// @param req body md.SetBasicReq true "上传需要修改的信息" +// @Success 200 {string} "success" +// @Failure 400 {object} md.Response "具体错误" +// @Router /api/im/setBasic [post] +func SetBasic(c *gin.Context) { + var req *md.SetBasicReq + if err1 := c.ShouldBindJSON(&req); err1 != nil { + e.OutErr(c, e.ERR_INVALID_ARGS, err1.Error()) + return + } + var isTure = true + isTure = svc.SysCfgSet(enum.IsAllowAddFriends, req.IsAllowAddFriends, enum.SysCfg.String(enum.IsAllowAddFriends)) + if !isTure { + e.OutErr(c, e.ERR_DB_ORM, "更新:"+enum.SysCfg.String(enum.IsAllowAddFriends)+",失败!") + return + } + isTure = svc.SysCfgSet(enum.IsAutoAddFriends, req.IsAutoAddFriends, enum.SysCfg.String(enum.IsAutoAddFriends)) + if !isTure { + e.OutErr(c, e.ERR_DB_ORM, "更新:"+enum.SysCfg.String(enum.IsAutoAddFriends)+",失败!") + return + } + isTure = svc.SysCfgSet(enum.ProhibitAddGroupMembers, req.ProhibitAddGroupMembers, enum.SysCfg.String(enum.ProhibitAddGroupMembers)) + if !isTure { + e.OutErr(c, e.ERR_DB_ORM, "更新:"+enum.SysCfg.String(enum.ProhibitAddGroupMembers)+",失败!") + return + } + isTure = svc.SysCfgSet(enum.ProhibitRemoveGroupMembers, req.ProhibitRemoveGroupMembers, enum.SysCfg.String(enum.ProhibitRemoveGroupMembers)) + if !isTure { + e.OutErr(c, e.ERR_DB_ORM, "更新:"+enum.SysCfg.String(enum.ProhibitAddGroupMembers)+",失败!") + return + } + isTure = svc.SysCfgSet(enum.ProhibitUpdateGroupName, req.ProhibitUpdateGroupName, enum.SysCfg.String(enum.ProhibitUpdateGroupName)) + if !isTure { + e.OutErr(c, e.ERR_DB_ORM, "更新:"+enum.SysCfg.String(enum.ProhibitUpdateGroupName)+",失败!") + return + } + isTure = svc.SysCfgSet(enum.ChatSensitiveWords, req.ChatSensitiveWords, enum.SysCfg.String(enum.ChatSensitiveWords)) + if !isTure { + e.OutErr(c, e.ERR_DB_ORM, "更新:"+enum.SysCfg.String(enum.ChatSensitiveWords)+",失败!") + return + } + e.OutSuc(c, "success", nil) +} diff --git a/app/hdl/im/hdl_customer_service.go b/app/hdl/im/hdl_customer_service.go new file mode 100644 index 0000000..ccb1ba8 --- /dev/null +++ b/app/hdl/im/hdl_customer_service.go @@ -0,0 +1,111 @@ +package im + +import ( + "applet/app/e" + md "applet/app/md/im" + svc "applet/app/svc/im" + "github.com/gin-gonic/gin" +) + +// PageCustomerService +// @Summary Im-客服(列表) +// @Tags 客服 +// @Description 客服(列表) +// @Accept json +// @Produce json +// @param Authorization header string true "验证参数Bearer和token空格拼接" +// @param req body md.PageCustomerServiceReq true +// @Success 200 {object} md.GetBasicResp "设置列表" +// @Failure 400 {object} md.Response "具体错误" +// @Router /api/im/pageCustomerService [POST] +func PageCustomerService(c *gin.Context) { + var req *md.PageCustomerServiceReq + if err1 := c.ShouldBindJSON(&req); err1 != nil { + e.OutErr(c, e.ERR_INVALID_ARGS, err1.Error()) + return + } + err, resp := svc.PageCustomerService(*req) + if err != nil { + e.OutErr(c, e.ERR, err.Error()) + return + } + e.OutSuc(c, resp, nil) +} + +// AddCustomerService +// @Summary Im-客服(新增) +// @Tags 客服 +// @Description 客服(新增) +// @Accept json +// @Produce json +// @param Authorization header string true "验证参数Bearer和token空格拼接" +// @param req body md.AddCustomerServiceReq true +// @Success 200 {string} "success" +// @Failure 400 {object} md.Response "具体错误" +// @Router /api/im/addCustomerService [post] +func AddCustomerService(c *gin.Context) { + var req *md.AddCustomerServiceReq + if err1 := c.ShouldBindJSON(&req); err1 != nil { + e.OutErr(c, e.ERR_INVALID_ARGS, err1.Error()) + return + } + + err := svc.AddCustomerService(*req) + if err != nil { + e.OutErr(c, e.ERR, err.Error()) + return + } + e.OutSuc(c, "success", nil) +} + +// SetCustomerServiceState +// @Summary Im-客服(更新状态) +// @Tags 客服 +// @Description 客服(更新状态) +// @Accept json +// @Produce json +// @param Authorization header string true "验证参数Bearer和token空格拼接" +// @param req body md.SetCustomerServiceStateReq true +// @Success 200 {string} "success" +// @Failure 400 {object} md.Response "具体错误" +// @Router /api/im/setCustomerServiceState [post] +func SetCustomerServiceState(c *gin.Context) { + var req *md.SetCustomerServiceStateReq + if err1 := c.ShouldBindJSON(&req); err1 != nil { + e.OutErr(c, e.ERR_INVALID_ARGS, err1.Error()) + return + } + + err := svc.SetCustomerServiceState(*req) + if err != nil { + e.OutErr(c, e.ERR, err.Error()) + return + } + e.OutSuc(c, "success", nil) +} + +// UpdateCustomerServiceMemo +// @Summary Im-客服(编辑备注) +// @Tags 客服 +// @Description 客服(编辑备注) +// @Accept json +// @Produce json +// @param Authorization header string true "验证参数Bearer和token空格拼接" +// @param req body md.UpdateCustomerServiceMemoReq true +// @Success 200 {string} "success" +// @Failure 400 {object} md.Response "具体错误" +// @Router /api/im/updateCustomerServiceMemo [post] +func UpdateCustomerServiceMemo(c *gin.Context) { + var req *md.UpdateCustomerServiceMemoReq + if err1 := c.ShouldBindJSON(&req); err1 != nil { + e.OutErr(c, e.ERR_INVALID_ARGS, err1.Error()) + return + } + + err := svc.UpdateCustomerServiceMemo(*req) + if err != nil { + e.OutErr(c, e.ERR, err.Error()) + return + } + e.OutSuc(c, "success", nil) +} diff --git a/app/hdl/im/hdl_emoticon.go b/app/hdl/im/hdl_emoticon.go new file mode 100644 index 0000000..5370b69 --- /dev/null +++ b/app/hdl/im/hdl_emoticon.go @@ -0,0 +1,137 @@ +package im + +import ( + "applet/app/e" + md "applet/app/md/im" + svc "applet/app/svc/im" + "github.com/gin-gonic/gin" +) + +// PageEmoticon +// @Summary Im-表情包(列表) +// @Tags 表情包 +// @Description 表情包(列表) +// @Accept json +// @Produce json +// @param Authorization header string true "验证参数Bearer和token空格拼接" +// @param req body md.PageEmoticonReq true +// @Success 200 {object} md.GetBasicResp "设置列表" +// @Failure 400 {object} md.Response "具体错误" +// @Router /api/im/pageEmoticon [POST] +func PageEmoticon(c *gin.Context) { + var req *md.PageEmoticonReq + if err1 := c.ShouldBindJSON(&req); err1 != nil { + e.OutErr(c, e.ERR_INVALID_ARGS, err1.Error()) + return + } + err, resp := svc.PageEmoticon(*req) + if err != nil { + e.OutErr(c, e.ERR, err.Error()) + return + } + e.OutSuc(c, resp, nil) +} + +// AddEmoticon +// @Summary Im-表情包(新增) +// @Tags 表情包 +// @Description 表情包(新增) +// @Accept json +// @Produce json +// @param Authorization header string true "验证参数Bearer和token空格拼接" +// @param req body md.AddEmoticonReq true +// @Success 200 {string} "success" +// @Failure 400 {object} md.Response "具体错误" +// @Router /api/im/addEmoticon [post] +func AddEmoticon(c *gin.Context) { + var req *md.AddEmoticonReq + if err1 := c.ShouldBindJSON(&req); err1 != nil { + e.OutErr(c, e.ERR_INVALID_ARGS, err1.Error()) + return + } + + err := svc.AddEmoticon(*req) + if err != nil { + e.OutErr(c, e.ERR, err.Error()) + return + } + e.OutSuc(c, "success", nil) +} + +// SetEmoticonState +// @Summary Im-表情包(更新状态) +// @Tags 表情包 +// @Description 表情包(新增) +// @Accept json +// @Produce json +// @param Authorization header string true "验证参数Bearer和token空格拼接" +// @param req body md.SetEmoticonStateReq true +// @Success 200 {string} "success" +// @Failure 400 {object} md.Response "具体错误" +// @Router /api/im/setEmoticonState [post] +func SetEmoticonState(c *gin.Context) { + var req *md.SetEmoticonStateReq + if err1 := c.ShouldBindJSON(&req); err1 != nil { + e.OutErr(c, e.ERR_INVALID_ARGS, err1.Error()) + return + } + + err := svc.SetEmoticonState(*req) + if err != nil { + e.OutErr(c, e.ERR, err.Error()) + return + } + e.OutSuc(c, "success", nil) +} + +// UpdateEmoticon +// @Summary Im-表情包(编辑) +// @Tags 表情包 +// @Description 表情包(编辑) +// @Accept json +// @Produce json +// @param Authorization header string true "验证参数Bearer和token空格拼接" +// @param req body md.UpdateEmoticonReq true +// @Success 200 {string} "success" +// @Failure 400 {object} md.Response "具体错误" +// @Router /api/im/updateEmoticon [post] +func UpdateEmoticon(c *gin.Context) { + var req *md.UpdateEmoticonReq + if err1 := c.ShouldBindJSON(&req); err1 != nil { + e.OutErr(c, e.ERR_INVALID_ARGS, err1.Error()) + return + } + + err := svc.UpdateEmoticon(*req) + if err != nil { + e.OutErr(c, e.ERR, err.Error()) + return + } + e.OutSuc(c, "success", nil) +} + +// DeleteEmoticon +// @Summary Im-表情包(删除) +// @Tags 表情包 +// @Description 表情包(删除) +// @Accept json +// @Produce json +// @param Authorization header string true "验证参数Bearer和token空格拼接" +// @param req body md.DeleteEmoticonReq true +// @Success 200 {string} "success" +// @Failure 400 {object} md.Response "具体错误" +// @Router /api/im/deleteEmoticon [post] +func DeleteEmoticon(c *gin.Context) { + var req *md.DeleteEmoticonReq + if err1 := c.ShouldBindJSON(&req); err1 != nil { + e.OutErr(c, e.ERR_INVALID_ARGS, err1.Error()) + return + } + + err := svc.DeleteEmoticon(*req) + if err != nil { + e.OutErr(c, e.ERR, err.Error()) + return + } + e.OutSuc(c, "success", nil) +} diff --git a/app/lib/qiniu/req_img_upload.go b/app/lib/qiniu/req_img_upload.go deleted file mode 100644 index d24f69b..0000000 --- a/app/lib/qiniu/req_img_upload.go +++ /dev/null @@ -1,55 +0,0 @@ -package qiniu - -import ( - "time" - - "github.com/qiniu/api.v7/v7/auth/qbox" - _ "github.com/qiniu/api.v7/v7/conf" - "github.com/qiniu/api.v7/v7/storage" - - "applet/app/md" - "applet/app/utils" -) - -// 请求图片上传地址信息 -func ReqImgUpload(f *md.FileCallback, callbackUrl string) interface{} { - if ext := utils.FileExt(f.FileName); ext == "png" || ext == "jpg" || ext == "jpeg" || ext == "gif" || ext == "bmp" || ext == "webp" { - f.Width = "$(imageInfo.width)" - f.Height = "$(imageInfo.height)" - } - f.Provider = "qiniu" - f.FileSize = "$(fsize)" - f.Hash = "$(etag)" - f.Bucket = "$(bucket)" - f.Mime = "$(mimeType)" - f.Time = utils.Int64ToStr(time.Now().Unix()) - f.Sign = Sign(f.Time) - putPolicy := storage.PutPolicy{ - Scope: BUCKET + ":" + f.FileName, // 使用覆盖方式时候必须请求里面有key,否则报错 - Expires: Expires, - ForceSaveKey: true, - SaveKey: f.FileName, - MimeLimit: "image/*", // 只允许上传图片 - CallbackURL: callbackUrl, - CallbackBody: utils.SerializeStr(f), - CallbackBodyType: "application/json", - } - return &struct { - Method string `json:"method"` - Key string `json:"key"` - Host string `json:"host"` - Token string `json:"token"` - }{Key: f.FileName, Method: "POST", Host: BUCKET_SCHEME + "://" + BUCKET_REGION, Token: putPolicy.UploadToken(qbox.NewMac(AK, SK))} -} - -/* -form表单上传 -地址 : http://upload-z2.qiniup.com -header - - Content-Type : multipart/form-data - -body : - - key : 文件名 - - token : 生成token - - file : 待上传文件 -*/ diff --git a/app/md/im/md_basic.go b/app/md/im/md_basic.go new file mode 100644 index 0000000..69406ad --- /dev/null +++ b/app/md/im/md_basic.go @@ -0,0 +1,19 @@ +package md + +type GetBasicResp struct { + IsAllowAddFriends string `json:"is_allow_add_friends" example:"是否允许加好友"` + IsAutoAddFriends string `json:"is_auto_add_friends" example:"是否自动加好友"` + ProhibitAddGroupMembers string `json:"prohibit_add_group_members" example:"是否禁止添加群成员"` + ProhibitRemoveGroupMembers string `json:"prohibit_remove_group_members" example:"是否禁止移出群成员"` + ProhibitUpdateGroupName string `json:"prohibit_update_group_name" example:"是否禁止修改群名称"` + ChatSensitiveWords string `json:"chat_sensitive_words" example:"聊天敏感词"` +} + +type SetBasicReq struct { + IsAllowAddFriends string `json:"is_allow_add_friends" example:"是否允许加好友"` + IsAutoAddFriends string `json:"is_auto_add_friends" example:"是否自动加好友"` + ProhibitAddGroupMembers string `json:"prohibit_add_group_members" example:"是否禁止添加群成员"` + ProhibitRemoveGroupMembers string `json:"prohibit_remove_group_members" example:"是否禁止移出群成员"` + ProhibitUpdateGroupName string `json:"prohibit_update_group_name" example:"是否禁止修改群名称"` + ChatSensitiveWords string `json:"chat_sensitive_words" example:"聊天敏感词"` +} diff --git a/app/md/im/md_customer_service.go b/app/md/im/md_customer_service.go new file mode 100644 index 0000000..ef64ffc --- /dev/null +++ b/app/md/im/md_customer_service.go @@ -0,0 +1,41 @@ +package md + +type PageCustomerServiceReq struct { + Page int `json:"page"` + PageSize int `json:"page_size"` + State int `json:"state"` //状态 + Memo string `json:"memo" example:"备注"` +} + +type PageCustomerServiceResp struct { + Page int `json:"page"` + PageSize int `json:"page_size"` + Total int64 `json:"total"` + List []struct { + Id int64 `json:"id"` //记录id + Phone string `json:"phone" example:"手机号"` + Nickname string `json:"nickname" example:"昵称"` + Weight int32 `json:"weight"` // 权重 + State int32 `json:"state"` // 状态(1:正常 2:冻结) + HaasUserNums int `json:"has_user_nums"` // 拥有用户数量 + Memo string `json:"memo" example:"备注"` // 备注 + CreateTime string `json:"create_time" example:"创建时间"` // 创建时间 + UpdateTime string `json:"update_time" example:"更新时间"` // 更新时间 + } `json:"list"` +} + +type AddCustomerServiceReq struct { + Phone string `json:"phone" example:"手机号"` + Memo string `json:"memo" example:"备注"` // 备注 + Weight int32 `json:"weight"` // 权重 +} + +type SetCustomerServiceStateReq struct { + Id int `json:"id"` // 拥有用户数量 + State int32 `json:"state"` // 状态 +} + +type UpdateCustomerServiceMemoReq struct { + Id int `json:"id"` // 拥有用户数量 + Memo string `json:"memo" example:"备注"` // 备注 +} diff --git a/app/md/im/md_emoticon.go b/app/md/im/md_emoticon.go new file mode 100644 index 0000000..9912a20 --- /dev/null +++ b/app/md/im/md_emoticon.go @@ -0,0 +1,48 @@ +package md + +type PageEmoticonReq struct { + Page int `json:"page"` + PageSize int `json:"page_size"` + State int `json:"state"` //状态 + Memo string `json:"memo" example:"备注"` +} + +type PageEmoticonResp struct { + Page int `json:"page"` + PageSize int `json:"page_size"` + Total int64 `json:"total"` + List []struct { + Id int64 `json:"id"` //记录id + Name string `json:"name" example:"名称"` + ImgUrl string `json:"img_url" example:"图片地址"` + Sort int `json:"Sort"` // 排序 + State int `json:"state"` // 状态0关闭,1开启 + Memo string `json:"memo" example:"备注"` // 备注 + CreateAt string `json:"create_at" example:"创建时间"` // 创建时间 + UpdateAt string `json:"update_at" example:"更新时间"` // 更新时间 + } `json:"list"` +} + +type AddEmoticonReq struct { + Name string `json:"name" example:"名称"` + ImgUrl string `json:"img_url" example:"图片地址"` + Memo string `json:"memo" example:"备注"` // 备注 + Sort int `json:"sort"` // 排序 +} + +type SetEmoticonStateReq struct { + Id int `json:"id"` // 拥有用户数量 + State int `json:"state"` // 状态 +} + +type UpdateEmoticonReq struct { + Id int `json:"id"` // 拥有用户数量 + Name string `json:"name" example:"名称"` + ImgUrl string `json:"img_url" example:"图片地址"` + Memo string `json:"memo" example:"备注"` // 备注 + Sort int `json:"sort"` // 排序 +} + +type DeleteEmoticonReq struct { + Id int `json:"id"` // 拥有用户数量 +} diff --git a/app/mw/mw_admin_permission.go b/app/mw/mw_admin_permission.go index 4165ee6..f25a22a 100644 --- a/app/mw/mw_admin_permission.go +++ b/app/mw/mw_admin_permission.go @@ -1,9 +1,9 @@ package mw import ( + "applet/app/cfg" "applet/app/e" "applet/app/enum" - "applet/app/md" "applet/app/svc" "applet/app/utils" "fmt" @@ -18,7 +18,7 @@ func CheckPermission(c *gin.Context) { if admin.IsSuperAdministrator == enum.IsSuperAdministratorTure { c.Next() } else { - rolePermissionKey := fmt.Sprintf(md.AdminRolePermissionKey, utils.AnyToString(admin.AdmId)) + rolePermissionKey := fmt.Sprintf(cfg.AdminRolePermissionKey, utils.AnyToString(admin.AdmId)) isHasPermission, err := svc.CheckUserRole(c, rolePermissionKey, c.Request.RequestURI, admin.AdmId) if err != nil { e.OutErr(c, e.ERR, err.Error()) diff --git a/app/router/router.go b/app/router/router.go index 587c62f..8ff9042 100644 --- a/app/router/router.go +++ b/app/router/router.go @@ -4,6 +4,7 @@ import ( "applet/app/cfg" "applet/app/hdl" "applet/app/hdl/comm" + "applet/app/hdl/im" "applet/app/hdl/institutional_management/egg_energy" "applet/app/hdl/institutional_management/public_platoon" "applet/app/hdl/marketing_applications/new_user_red_package" @@ -56,6 +57,7 @@ func route(r *gin.RouterGroup) { r.POST("/login", hdl.Login) r.Use(mw.Auth) // 以下接口需要JWT验证 rComm(r.Group("/comm")) + rIm(r.Group("/im")) r.Use(mw.CheckPermission) // 检测权限 rInstitutionalManagement(r.Group("/institutionalManagement")) rMarketingApplications(r.Group("/marketingApplications")) @@ -186,6 +188,20 @@ func rMemberCenter(r *gin.RouterGroup) { // 会员中心 } } +func rIm(r *gin.RouterGroup) { + r.GET("/getBasic", im.GetBasic) + r.POST("/setBasic", im.SetBasic) + r.POST("/pageEmoticon", im.PageEmoticon) + r.POST("/addEmoticon", im.AddEmoticon) + r.POST("/setEmoticonState", im.SetEmoticonState) + r.POST("/updateEmoticon", im.UpdateEmoticon) + r.POST("/deleteEmoticon", im.DeleteEmoticon) + r.POST("/pageCustomerService", im.PageCustomerService) + r.POST("/addCustomerService", im.AddCustomerService) + r.POST("/setCustomerServiceState", im.SetCustomerServiceState) + r.POST("/updateCustomerServiceMemo", im.UpdateCustomerServiceMemo) +} + func rComm(r *gin.RouterGroup) { r.POST("/getMenuList", comm.MenuList) // 获取菜单栏列表 r.GET("/getOssUrl", comm.GetOssUrl) // 获取阿里云上传PutObject所需的签名URL diff --git a/app/svc/im/svc_customer_service.go b/app/svc/im/svc_customer_service.go new file mode 100644 index 0000000..8627c14 --- /dev/null +++ b/app/svc/im/svc_customer_service.go @@ -0,0 +1,126 @@ +package svc + +import ( + "applet/app/db" + "applet/app/db/im/model" + md "applet/app/md/im" + "errors" + "time" +) + +func PageCustomerService(req md.PageCustomerServiceReq) (err error, resp md.PageCustomerServiceResp) { + sess := db.DbIm.Where("1=1") + if req.Memo != "" { + sess.And("memo LIKE ?", "%"+req.Memo+"%") + } + if req.State != 0 { + sess.And("memo = ?", req.State) + } + var mm []*model.CustomerService + resp.Total, err = sess.Limit(req.PageSize, (req.Page-1)*req.PageSize).Asc("a.id").FindAndCount(&mm) + if err != nil { + return + } + resp.Page = req.Page + resp.PageSize = req.PageSize + for _, v := range mm { + var user model.User + _, err = db.DbIm.Where("`id`=?", v.Uid).Get(&user) + if err != nil { + return + } + + resp.List = append(resp.List, struct { + Id int64 `json:"id"` //记录id + Phone string `json:"phone" example:"手机号"` + Nickname string `json:"nickname" example:"昵称"` + Weight int32 `json:"weight"` // 权重 + State int32 `json:"state"` // 状态 + HaasUserNums int `json:"has_user_nums"` // 拥有用户数量 + Memo string `json:"memo" example:"备注"` // 备注 + CreateTime string `json:"create_time" example:"创建时间"` // 创建时间 + UpdateTime string `json:"update_time" example:"更新时间"` // 更新时间 + }{ + Id: v.Id, + Phone: user.PhoneNumber, + Nickname: user.Nickname, + Weight: v.Weight, + State: v.State, + HaasUserNums: v.HasUserNums, + Memo: v.Memo, + CreateTime: v.CreateTime.Format("2006-01-02 15:04:05"), + UpdateTime: v.UpdateTime.Format("2006-01-02 15:04:05"), + }) + } + return +} + +func AddCustomerService(req md.AddCustomerServiceReq) (err error) { + var user model.User + has, err := db.DbIm.Where("`phone_number`=?", req.Phone).Get(&user) + if err != nil { + return + } + if !has { + return errors.New("未查询到对应手机号账号记录") + } + + var customerService model.CustomerService + has, err = db.DbIm.Where("`uid`=?", user.Id).Get(&customerService) + if err != nil { + return + } + if has { + return errors.New("该账号已是客服!") + } + + cfg := model.CustomerService{ + Uid: user.Id, + Weight: req.Weight, + State: 1, + HasUserNums: 0, + Memo: req.Memo, + CreateTime: time.Time{}, + UpdateTime: time.Time{}, + } + _, err = db.DbIm.InsertOne(&cfg) + if err != nil { + return + } + + return +} + +func SetCustomerServiceState(req md.SetCustomerServiceStateReq) (err error) { + var customerService model.CustomerService + has, err := db.DbIm.ID(req.Id).Get(&customerService) + if err != nil { + return + } + if has { + return errors.New("未查询到对应记录!") + } + customerService.State = req.State + _, err = db.DbIm.ID(req.Id).Cols("state").Update(&customerService) + if err != nil { + return + } + return +} + +func UpdateCustomerServiceMemo(req md.UpdateCustomerServiceMemoReq) (err error) { + var customerService model.CustomerService + has, err := db.DbIm.ID(req.Id).Get(&customerService) + if err != nil { + return + } + if has { + return errors.New("未查询到对应记录!") + } + customerService.Memo = req.Memo + _, err = db.DbIm.ID(req.Id).Cols("memo").Update(&customerService) + if err != nil { + return + } + return +} diff --git a/app/svc/im/svc_emoticon.go b/app/svc/im/svc_emoticon.go new file mode 100644 index 0000000..a8e57f5 --- /dev/null +++ b/app/svc/im/svc_emoticon.go @@ -0,0 +1,98 @@ +package svc + +import ( + "applet/app/db" + "applet/app/db/im/model" + md "applet/app/md/im" + "errors" + "time" +) + +func PageEmoticon(req md.PageEmoticonReq) (err error, resp md.PageEmoticonResp) { + sess := db.DbIm.Where("1=1") + if req.Memo != "" { + sess.And("memo LIKE ?", "%"+req.Memo+"%") + } + if req.State != 0 { + sess.And("memo = ?", req.State) + } + var mm []*model.Emoticon + resp.Total, err = sess.Limit(req.PageSize, (req.Page-1)*req.PageSize).Asc("a.id").FindAndCount(&mm) + if err != nil { + return + } + resp.Page = req.Page + resp.PageSize = req.PageSize + for _, v := range mm { + resp.List = append(resp.List, struct { + Id int64 `json:"id"` //记录id + Name string `json:"name" example:"名称"` + ImgUrl string `json:"img_url" example:"图片地址"` + Sort int `json:"Sort"` // 权重 + State int `json:"state"` // 状态0关闭,1开启 + Memo string `json:"memo" example:"备注"` // 备注 + CreateAt string `json:"create_at" example:"创建时间"` // 创建时间 + UpdateAt string `json:"update_at" example:"更新时间"` // 更新时间 + }{Id: v.Id, Name: v.Name, ImgUrl: v.ImgUrl, Sort: v.Sort, State: v.State, Memo: v.Memo, CreateAt: v.CreateAt, UpdateAt: v.UpdateAt}) + } + return +} + +func AddEmoticon(req md.AddEmoticonReq) (err error) { + cfg := model.Emoticon{ + Name: req.Name, + ImgUrl: req.ImgUrl, + Memo: req.Memo, + Sort: req.Sort, + State: 1, + CreateAt: time.Now().Format("2006-01-02 15:04:05"), + UpdateAt: time.Now().Format("2006-01-02 15:04:05"), + } + _, err = db.DbIm.InsertOne(&cfg) + if err != nil { + return + } + return +} + +func SetEmoticonState(req md.SetEmoticonStateReq) (err error) { + var customerService model.Emoticon + has, err := db.DbIm.ID(req.Id).Get(&customerService) + if err != nil { + return + } + if has { + return errors.New("未查询到对应记录!") + } + customerService.State = req.State + _, err = db.DbIm.ID(req.Id).Cols("state").Update(&customerService) + if err != nil { + return + } + return +} + +func UpdateEmoticon(req md.UpdateEmoticonReq) (err error) { + var customerService model.Emoticon + has, err := db.DbIm.ID(req.Id).Get(&customerService) + if err != nil { + return + } + if has { + return errors.New("未查询到对应记录!") + } + customerService.Name = req.Name + customerService.ImgUrl = req.ImgUrl + customerService.Sort = req.Sort + customerService.Memo = req.Memo + _, err = db.DbIm.ID(req.Id).Update(&customerService) + if err != nil { + return + } + return +} + +func DeleteEmoticon(req md.DeleteEmoticonReq) (err error) { + _, err = db.DbIm.Where("id = ?", req.Id).Delete(&model.Emoticon{}) + return +} diff --git a/app/svc/im/svc_sys_cfg_get.go b/app/svc/im/svc_sys_cfg_get.go new file mode 100644 index 0000000..92dc38c --- /dev/null +++ b/app/svc/im/svc_sys_cfg_get.go @@ -0,0 +1,58 @@ +package svc + +import ( + cfgMd "applet/app/cfg" + db2 "applet/app/db" + db "applet/app/db/im" + "applet/app/utils/cache" +) + +// 多条记录获取 +func SysCfgFind(keys ...string) map[string]string { + tmp := SysCfgFindComm(keys...) + return tmp +} + +// SysCfgFindComm get cfg by master id +func SysCfgFindComm(keys ...string) map[string]string { + res := map[string]string{} + cfgList, _ := db.SysCfgGetAll(db2.DbIm) + if cfgList == nil { + return nil + } + for _, v := range *cfgList { + res[v.Key] = v.Val + } + + if len(keys) == 0 { + return res + } + tmp := map[string]string{} + for _, v := range keys { + if val, ok := res[v]; ok { + tmp[v] = val + } else { + tmp[v] = "" + } + } + return tmp +} + +// SysCfgCleanCache 清理系统配置信息 +func SysCfgCleanCache() { + cache.SelectDb(1) //TODO::IM统一放1号库 + cache.Del(cfgMd.IM_KEY_SYS_CFG_CACHE) +} + +// SysCfgSet 写入系统设置 +func SysCfgSet(key, val, memo string) bool { + cfg, err := db.SysCfgGetOne(db2.DbIm, key) + if err != nil || cfg == nil { + return db.SysCfgInsert(db2.DbIm, key, val, memo) + } + if memo != "" && cfg.Memo != memo { + cfg.Memo = memo + } + SysCfgCleanCache() + return db.SysCfgUpdate(db2.DbIm, key, val, cfg.Memo) +} diff --git a/app/svc/svc_login.go b/app/svc/svc_login.go index b3bc300..74296e2 100644 --- a/app/svc/svc_login.go +++ b/app/svc/svc_login.go @@ -1,8 +1,8 @@ package svc import ( + "applet/app/cfg" "applet/app/lib/auth" - "applet/app/md" "applet/app/utils/cache" "applet/app/utils/logx" "code.fnuoos.com/EggPlanet/egg_models.git/src/model" @@ -22,7 +22,7 @@ func HandleLoginToken(cacheKey string, admin *model.Admin) (string, error) { return "", err } // 缓存token - _, err = cache.SetEx(cacheKey, token, md.JwtTokenCacheTime) + _, err = cache.SetEx(cacheKey, token, cfg.JwtTokenCacheTime) if err != nil { return "", err } diff --git a/app/svc/svc_role.go b/app/svc/svc_role.go index 3e3f8e6..79bafd1 100644 --- a/app/svc/svc_role.go +++ b/app/svc/svc_role.go @@ -1,6 +1,7 @@ package svc import ( + "applet/app/cfg" "applet/app/db" "applet/app/md" "applet/app/utils" @@ -48,7 +49,7 @@ func CheckUserRole(c *gin.Context, cacheKey, uri string, admId int) (isHasPermis return isHasPermission, err1 } rolePermissionString = string(marshal) - _, err = cache.SetEx(cacheKey, rolePermissionString, md.AdminRolePermissionCacheTime) + _, err = cache.SetEx(cacheKey, rolePermissionString, cfg.AdminRolePermissionCacheTime) } if utils.InArr(uri, rolePermission) { @@ -144,7 +145,7 @@ func RoleBindPermissionGroup(c *gin.Context, req md.RoleBindPermissionGroupReq) var data []model.AdminRole engine.Where("role_id=?", role.Id).Find(&data) for _, v := range data { - rolePermissionKey := fmt.Sprintf(md.AdminRolePermissionKey, utils.AnyToString(v.AdmId)) + rolePermissionKey := fmt.Sprintf(cfg.AdminRolePermissionKey, utils.AnyToString(v.AdmId)) cache.Del(rolePermissionKey) } return nil diff --git a/app/utils/cache/redis.go b/app/utils/cache/redis.go index 2199787..9d666a6 100644 --- a/app/utils/cache/redis.go +++ b/app/utils/cache/redis.go @@ -132,6 +132,10 @@ func SetEx(key string, data interface{}, ttl int) (interface{}, error) { return Do("SETEX", key, ttl, data) } +func SelectDb(db int) (interface{}, error) { + return Do("SELECT", db) +} + func SetJson(key string, data interface{}, ttl int) bool { c, err := json.Marshal(data) if err != nil { @@ -402,8 +406,7 @@ func Scan(cursor int64, pattern string, count int64) (int64, []string, error) { return newCursor, items, nil } - func LPushMax(key string, data ...interface{}) (interface{}, error) { // set return Do("LPUSH", key, data) -} \ No newline at end of file +} diff --git a/cmd/task/main.go b/cmd/task/main.go index ec3c112..e6daccb 100644 --- a/cmd/task/main.go +++ b/cmd/task/main.go @@ -25,6 +25,11 @@ func init() { if err := db.InitDB(&baseDb); err != nil { panic(err) } + baseImDb := *cfg.IMDB + baseImDb.Path = fmt.Sprintf(cfg.IMDB.Path, cfg.IMDB.Name) + if err := db.InitImDB(&baseImDb); err != nil { + panic(err) + } utils.CurlDebug = true //cfg.InitMemCache() } diff --git a/etc/cfg.yml b/etc/cfg.yml index 1240dec..cad6db0 100644 --- a/etc/cfg.yml +++ b/etc/cfg.yml @@ -20,6 +20,17 @@ db: max_idle_conns: 100 path: 'tmp/%s.log' +im_db: + host: '119.23.182.117:3306' + name: 'egg' + user: 'root' + psw: 'Fnuo123com@' + show_log: true + max_lifetime: 30 + max_open_conns: 100 + max_idle_conns: 100 + path: 'tmp/%s.log' + # 日志 log: app_name: 'applet' diff --git a/main.go b/main.go index 7917546..0ea3d2e 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,9 @@ func init() { if err := db.InitDB(cfg.DB); err != nil { // 主数据库初始化 panic(err) } + if err := db.InitImDB(cfg.IMDB); err != nil { // IM主数据库初始化 + panic(err) + } } fmt.Println("init success")