From ffc1bcabab6acfb133805c20672f95a56eaef0ea Mon Sep 17 00:00:00 2001 From: IRONICBo <47499836+IRONICBo@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:10:02 +0800 Subject: [PATCH 1/6] feat: Add slack support. Signed-off-by: IRONICBo <47499836+IRONICBo@users.noreply.github.com> --- server/cmd/dbmigration/main.go | 3 + server/cmd/gendal/main.go | 3 + server/cmd/gendao/main.go | 3 + server/go.mod | 7 +- server/go.sum | 18 +- .../dal/dao/gen_customer_slack_dao.go | 584 ++++++++++++++++++ server/internal/dal/gen/customer_slack.gen.go | 516 ++++++++++++++++ server/internal/dal/gen/gen.go | 72 ++- .../models/customer_roles/customer_slack.go | 50 ++ server/internal/msg/slack_cmd/slack.go | 64 ++ .../internal/msg/staff_dispatch/dispatch.go | 15 + server/internal/service/slack.go | 95 ++- server/main.go | 2 + 13 files changed, 1393 insertions(+), 39 deletions(-) create mode 100755 server/internal/dal/dao/gen_customer_slack_dao.go create mode 100644 server/internal/dal/gen/customer_slack.gen.go create mode 100644 server/internal/models/customer_roles/customer_slack.go create mode 100644 server/internal/msg/slack_cmd/slack.go create mode 100644 server/internal/msg/staff_dispatch/dispatch.go diff --git a/server/cmd/dbmigration/main.go b/server/cmd/dbmigration/main.go index 3253657..c1aad86 100644 --- a/server/cmd/dbmigration/main.go +++ b/server/cmd/dbmigration/main.go @@ -19,6 +19,7 @@ import ( "github.com/OpenIMSDK/OpenKF/server/internal/config" "github.com/OpenIMSDK/OpenKF/server/internal/conn/db" + customerroles "github.com/OpenIMSDK/OpenKF/server/internal/models/customer_roles" systemroles "github.com/OpenIMSDK/OpenKF/server/internal/models/system_roles" "github.com/OpenIMSDK/OpenKF/server/internal/utils" "github.com/OpenIMSDK/OpenKF/server/pkg/log" @@ -52,6 +53,8 @@ func main() { systemroles.SysCustomer{}, systemroles.SysCommunity{}, systemroles.SysBot{}, + + customerroles.CustomerSlack{}, } // drop tables if exist. diff --git a/server/cmd/gendal/main.go b/server/cmd/gendal/main.go index dad3278..48e750b 100644 --- a/server/cmd/gendal/main.go +++ b/server/cmd/gendal/main.go @@ -19,6 +19,7 @@ import ( "gorm.io/gen" + customerroles "github.com/OpenIMSDK/OpenKF/server/internal/models/customer_roles" systemroles "github.com/OpenIMSDK/OpenKF/server/internal/models/system_roles" ) @@ -38,6 +39,8 @@ func main() { systemroles.SysCustomer{}, systemroles.SysCommunity{}, systemroles.SysBot{}, + + customerroles.CustomerSlack{}, } // Generate basic dao diff --git a/server/cmd/gendao/main.go b/server/cmd/gendao/main.go index 699f85a..c34ad1f 100644 --- a/server/cmd/gendao/main.go +++ b/server/cmd/gendao/main.go @@ -18,6 +18,7 @@ import ( "flag" "github.com/OpenIMSDK/OpenKF/server/cmd/gendao/pkg" + customerroles "github.com/OpenIMSDK/OpenKF/server/internal/models/customer_roles" systemroles "github.com/OpenIMSDK/OpenKF/server/internal/models/system_roles" ) @@ -30,6 +31,8 @@ func main() { systemroles.SysCustomer{}, systemroles.SysCommunity{}, systemroles.SysBot{}, + + customerroles.CustomerSlack{}, } for _, model := range models { diff --git a/server/go.mod b/server/go.mod index 6f8fc3c..e84c7a8 100644 --- a/server/go.mod +++ b/server/go.mod @@ -5,7 +5,6 @@ go 1.19 require ( github.com/Delta456/box-cli-maker/v2 v2.3.0 github.com/antonfisher/nested-logrus-formatter v1.3.1 - github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 github.com/gin-gonic/gin v1.9.1 github.com/go-redis/redis/v8 v8.11.5 github.com/go-resty/resty/v2 v2.7.0 @@ -16,6 +15,7 @@ require ( github.com/minio/minio-go/v7 v7.0.57 github.com/pkg/errors v0.9.1 github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 + github.com/shomali11/slacker v1.4.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.16.0 github.com/swaggo/files v1.0.1 @@ -48,6 +48,7 @@ require ( github.com/goccy/go-json v0.10.2 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gookit/color v1.5.3 // indirect + github.com/gorilla/websocket v1.4.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect @@ -72,8 +73,12 @@ require ( github.com/onsi/gomega v1.27.6 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/robfig/cron v1.2.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rs/xid v1.5.0 // indirect + github.com/shomali11/commander v0.0.0-20220716022157-b5248c76541a // indirect + github.com/shomali11/proper v0.0.0-20180607004733-233a9a872c30 // indirect + github.com/slack-go/slack v0.12.1 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/server/go.sum b/server/go.sum index 44d8d13..e7099da 100644 --- a/server/go.sum +++ b/server/go.sum @@ -77,8 +77,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc= -github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yHzKvwhsiSIWmbAYM3Dr9AEEbER2dVayfBkg= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= @@ -117,6 +115,8 @@ github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSM github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= +github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= @@ -163,6 +163,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -189,6 +190,8 @@ github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= github.com/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE= github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -293,13 +296,23 @@ github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5H github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= +github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/shomali11/commander v0.0.0-20220716022157-b5248c76541a h1:NCmAZOmyqKwf+0KzhY6I6CPndU3qkLRp47RwTyLdMW8= +github.com/shomali11/commander v0.0.0-20220716022157-b5248c76541a/go.mod h1:bYyJw/Aj9fK+qoFmRbPJeWsDgq7WGO8f/Qof95qPug4= +github.com/shomali11/proper v0.0.0-20180607004733-233a9a872c30 h1:56awf1OXG6Jc2Pk1saojpCzpzkoBvlqecCyNLY+wwkc= +github.com/shomali11/proper v0.0.0-20180607004733-233a9a872c30/go.mod h1:O723XwIZBX3FR45rBic/Eyp/DKo/YtchYFURzpUWY2c= +github.com/shomali11/slacker v1.4.1 h1:t2R5Drx1MJXmgNejhf2cIfVmFfwEu4tRSbpYn4jfwwI= +github.com/shomali11/slacker v1.4.1/go.mod h1:Crk6eTJrfV158YuGDbbJ8yzRS/guH7Snw4c9c/nIuE4= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/slack-go/slack v0.12.1 h1:X97b9g2hnITDtNsNe5GkGx6O2/Sz/uC20ejRZN6QxOw= +github.com/slack-go/slack v0.12.1/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= @@ -313,6 +326,7 @@ github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1Fof github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= diff --git a/server/internal/dal/dao/gen_customer_slack_dao.go b/server/internal/dal/dao/gen_customer_slack_dao.go new file mode 100755 index 0000000..9d81684 --- /dev/null +++ b/server/internal/dal/dao/gen_customer_slack_dao.go @@ -0,0 +1,584 @@ +// Copyright © 2023 OpenIM open source community. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dao + +import ( + "context" + "time" + + "github.com/OpenIMSDK/OpenKF/server/internal/conn/db" + "github.com/OpenIMSDK/OpenKF/server/internal/dal/cache" + "github.com/OpenIMSDK/OpenKF/server/internal/dal/gen" + customerroles "github.com/OpenIMSDK/OpenKF/server/internal/models/customer_roles" +) + +// CustomerSlackDao customerslack dao. +type CustomerSlackDao struct { + Dao +} + +// NewCustomerSlackDao return a customerslack dao. +func NewCustomerSlackDao() *CustomerSlackDao { + query := gen.Use(db.GetMysqlDB()) + cache := cache.Use(db.GetRedis()) + + return &CustomerSlackDao{ + Dao: Dao{ + ctx: context.Background(), + query: query, + cache: &cache, + }, + } +} + +// Create create one or multi models. +func (d *CustomerSlackDao) Create(m ...*customerroles.CustomerSlack) error { + return d.query.WithContext(d.ctx).CustomerSlack.Create(m...) +} + +// First get first matched result. +func (d *CustomerSlackDao) First() (*customerroles.CustomerSlack, error) { + return d.query.WithContext(d.ctx).CustomerSlack.First() +} + +// FindAll get all matched results. +func (d *CustomerSlackDao) FindAll() ([]*customerroles.CustomerSlack, error) { + return d.query.WithContext(d.ctx).CustomerSlack.Find() +} + +// FindFirstById get first matched result by id. +func (d *CustomerSlackDao) FindFirstById(id uint) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Id.Eq(id)).First() +} + +// FindByIdPage get page by Id. +func (d *CustomerSlackDao) FindByIdPage(id uint, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Id.Eq(id)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByCreatedAt get first matched result by createdat. +func (d *CustomerSlackDao) FindFirstByCreatedAt(createdat time.Time) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.CreatedAt.Eq(createdat)).First() +} + +// FindByCreatedAtPage get page by CreatedAt. +func (d *CustomerSlackDao) FindByCreatedAtPage(createdat time.Time, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.CreatedAt.Eq(createdat)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByUpdatedAt get first matched result by updatedat. +func (d *CustomerSlackDao) FindFirstByUpdatedAt(updatedat time.Time) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.UpdatedAt.Eq(updatedat)).First() +} + +// FindByUpdatedAtPage get page by UpdatedAt. +func (d *CustomerSlackDao) FindByUpdatedAtPage(updatedat time.Time, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.UpdatedAt.Eq(updatedat)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByDeletedAt get first matched result by deletedat. +func (d *CustomerSlackDao) FindFirstByDeletedAt(deletedat time.Time) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.DeletedAt.Eq(deletedat)).First() +} + +// FindByDeletedAtPage get page by DeletedAt. +func (d *CustomerSlackDao) FindByDeletedAtPage(deletedat time.Time, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.DeletedAt.Eq(deletedat)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByUUID get first matched result by uuid. +func (d *CustomerSlackDao) FindFirstByUUID(uuid string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.UUID.Eq(uuid)).First() +} + +// FindByUUIDPage get page by UUID. +func (d *CustomerSlackDao) FindByUUIDPage(uuid string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.UUID.Eq(uuid)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByEmail get first matched result by email. +func (d *CustomerSlackDao) FindFirstByEmail(email string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Email.Eq(email)).First() +} + +// FindByEmailPage get page by Email. +func (d *CustomerSlackDao) FindByEmailPage(email string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Email.Eq(email)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByNickname get first matched result by nickname. +func (d *CustomerSlackDao) FindFirstByNickname(nickname string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Nickname.Eq(nickname)).First() +} + +// FindByNicknamePage get page by Nickname. +func (d *CustomerSlackDao) FindByNicknamePage(nickname string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Nickname.Eq(nickname)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByAvatar get first matched result by avatar. +func (d *CustomerSlackDao) FindFirstByAvatar(avatar string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Avatar.Eq(avatar)).First() +} + +// FindByAvatarPage get page by Avatar. +func (d *CustomerSlackDao) FindByAvatarPage(avatar string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Avatar.Eq(avatar)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByDescription get first matched result by description. +func (d *CustomerSlackDao) FindFirstByDescription(description string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Description.Eq(description)).First() +} + +// FindByDescriptionPage get page by Description. +func (d *CustomerSlackDao) FindByDescriptionPage(description string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Description.Eq(description)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByFirstName get first matched result by firstname. +func (d *CustomerSlackDao) FindFirstByFirstName(firstname string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.FirstName.Eq(firstname)).First() +} + +// FindByFirstNamePage get page by FirstName. +func (d *CustomerSlackDao) FindByFirstNamePage(firstname string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.FirstName.Eq(firstname)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByLastName get first matched result by lastname. +func (d *CustomerSlackDao) FindFirstByLastName(lastname string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.LastName.Eq(lastname)).First() +} + +// FindByLastNamePage get page by LastName. +func (d *CustomerSlackDao) FindByLastNamePage(lastname string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.LastName.Eq(lastname)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByRealName get first matched result by realname. +func (d *CustomerSlackDao) FindFirstByRealName(realname string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.RealName.Eq(realname)).First() +} + +// FindByRealNamePage get page by RealName. +func (d *CustomerSlackDao) FindByRealNamePage(realname string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.RealName.Eq(realname)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByRealNameNormalized get first matched result by realnamenormalized. +func (d *CustomerSlackDao) FindFirstByRealNameNormalized(realnamenormalized string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.RealNameNormalized.Eq(realnamenormalized)).First() +} + +// FindByRealNameNormalizedPage get page by RealNameNormalized. +func (d *CustomerSlackDao) FindByRealNameNormalizedPage(realnamenormalized string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.RealNameNormalized.Eq(realnamenormalized)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByDisplayName get first matched result by displayname. +func (d *CustomerSlackDao) FindFirstByDisplayName(displayname string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.DisplayName.Eq(displayname)).First() +} + +// FindByDisplayNamePage get page by DisplayName. +func (d *CustomerSlackDao) FindByDisplayNamePage(displayname string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.DisplayName.Eq(displayname)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByDisplayNameNormalized get first matched result by displaynamenormalized. +func (d *CustomerSlackDao) FindFirstByDisplayNameNormalized(displaynamenormalized string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.DisplayNameNormalized.Eq(displaynamenormalized)).First() +} + +// FindByDisplayNameNormalizedPage get page by DisplayNameNormalized. +func (d *CustomerSlackDao) FindByDisplayNameNormalizedPage(displaynamenormalized string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.DisplayNameNormalized.Eq(displaynamenormalized)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstBySkype get first matched result by skype. +func (d *CustomerSlackDao) FindFirstBySkype(skype string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Skype.Eq(skype)).First() +} + +// FindBySkypePage get page by Skype. +func (d *CustomerSlackDao) FindBySkypePage(skype string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Skype.Eq(skype)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByPhone get first matched result by phone. +func (d *CustomerSlackDao) FindFirstByPhone(phone string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Phone.Eq(phone)).First() +} + +// FindByPhonePage get page by Phone. +func (d *CustomerSlackDao) FindByPhonePage(phone string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Phone.Eq(phone)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByImage24 get first matched result by image24. +func (d *CustomerSlackDao) FindFirstByImage24(image24 string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Image24.Eq(image24)).First() +} + +// FindByImage24Page get page by Image24. +func (d *CustomerSlackDao) FindByImage24Page(image24 string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Image24.Eq(image24)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByImage32 get first matched result by image32. +func (d *CustomerSlackDao) FindFirstByImage32(image32 string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Image32.Eq(image32)).First() +} + +// FindByImage32Page get page by Image32. +func (d *CustomerSlackDao) FindByImage32Page(image32 string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Image32.Eq(image32)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByImage48 get first matched result by image48. +func (d *CustomerSlackDao) FindFirstByImage48(image48 string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Image48.Eq(image48)).First() +} + +// FindByImage48Page get page by Image48. +func (d *CustomerSlackDao) FindByImage48Page(image48 string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Image48.Eq(image48)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByImage72 get first matched result by image72. +func (d *CustomerSlackDao) FindFirstByImage72(image72 string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Image72.Eq(image72)).First() +} + +// FindByImage72Page get page by Image72. +func (d *CustomerSlackDao) FindByImage72Page(image72 string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Image72.Eq(image72)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByImage192 get first matched result by image192. +func (d *CustomerSlackDao) FindFirstByImage192(image192 string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Image192.Eq(image192)).First() +} + +// FindByImage192Page get page by Image192. +func (d *CustomerSlackDao) FindByImage192Page(image192 string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Image192.Eq(image192)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByImage512 get first matched result by image512. +func (d *CustomerSlackDao) FindFirstByImage512(image512 string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Image512.Eq(image512)).First() +} + +// FindByImage512Page get page by Image512. +func (d *CustomerSlackDao) FindByImage512Page(image512 string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Image512.Eq(image512)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByImageOriginal get first matched result by imageoriginal. +func (d *CustomerSlackDao) FindFirstByImageOriginal(imageoriginal string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.ImageOriginal.Eq(imageoriginal)).First() +} + +// FindByImageOriginalPage get page by ImageOriginal. +func (d *CustomerSlackDao) FindByImageOriginalPage(imageoriginal string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.ImageOriginal.Eq(imageoriginal)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByTitle get first matched result by title. +func (d *CustomerSlackDao) FindFirstByTitle(title string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Title.Eq(title)).First() +} + +// FindByTitlePage get page by Title. +func (d *CustomerSlackDao) FindByTitlePage(title string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Title.Eq(title)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByBotID get first matched result by botid. +func (d *CustomerSlackDao) FindFirstByBotID(botid string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.BotID.Eq(botid)).First() +} + +// FindByBotIDPage get page by BotID. +func (d *CustomerSlackDao) FindByBotIDPage(botid string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.BotID.Eq(botid)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByApiAppID get first matched result by apiappid. +func (d *CustomerSlackDao) FindFirstByApiAppID(apiappid string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.ApiAppID.Eq(apiappid)).First() +} + +// FindByApiAppIDPage get page by ApiAppID. +func (d *CustomerSlackDao) FindByApiAppIDPage(apiappid string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.ApiAppID.Eq(apiappid)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByStatusText get first matched result by statustext. +func (d *CustomerSlackDao) FindFirstByStatusText(statustext string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.StatusText.Eq(statustext)).First() +} + +// FindByStatusTextPage get page by StatusText. +func (d *CustomerSlackDao) FindByStatusTextPage(statustext string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.StatusText.Eq(statustext)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByStatusEmoji get first matched result by statusemoji. +func (d *CustomerSlackDao) FindFirstByStatusEmoji(statusemoji string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.StatusEmoji.Eq(statusemoji)).First() +} + +// FindByStatusEmojiPage get page by StatusEmoji. +func (d *CustomerSlackDao) FindByStatusEmojiPage(statusemoji string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.StatusEmoji.Eq(statusemoji)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByStatusExpiration get first matched result by statusexpiration. +func (d *CustomerSlackDao) FindFirstByStatusExpiration(statusexpiration int) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.StatusExpiration.Eq(statusexpiration)).First() +} + +// FindByStatusExpirationPage get page by StatusExpiration. +func (d *CustomerSlackDao) FindByStatusExpirationPage(statusexpiration int, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.StatusExpiration.Eq(statusexpiration)).FindByPage(offset, limit) + + return result, count, err +} + +// FindFirstByTeam get first matched result by team. +func (d *CustomerSlackDao) FindFirstByTeam(team string) (*customerroles.CustomerSlack, error) { + m := d.query.CustomerSlack + + return m.WithContext(d.ctx).Where(m.Team.Eq(team)).First() +} + +// FindByTeamPage get page by Team. +func (d *CustomerSlackDao) FindByTeamPage(team string, offset int, limit int) ([]*customerroles.CustomerSlack, int64, error) { + m := d.query.CustomerSlack + + result, count, err := m.WithContext(d.ctx).Where(m.Team.Eq(team)).FindByPage(offset, limit) + + return result, count, err +} + +// Update update model. +func (d *CustomerSlackDao) Update(m *customerroles.CustomerSlack) error { + res, err := d.query.WithContext(d.ctx).CustomerSlack.Updates(m) + if err != nil && res.Error != nil { + return err + } + + return nil +} + +// Delete delete model. +func (d *CustomerSlackDao) Delete(m ...*customerroles.CustomerSlack) error { + res, err := d.query.WithContext(d.ctx).CustomerSlack.Delete(m...) + if err != nil && res.Error != nil { + return err + } + + return nil +} + +// Count count matched records. +func (d *CustomerSlackDao) Count() (int64, error) { + return d.query.WithContext(d.ctx).CustomerSlack.Count() +} + +/////////////////////////////////////////////////////////// +// Append your code here. // +/////////////////////////////////////////////////////////// diff --git a/server/internal/dal/gen/customer_slack.gen.go b/server/internal/dal/gen/customer_slack.gen.go new file mode 100644 index 0000000..a010f5c --- /dev/null +++ b/server/internal/dal/gen/customer_slack.gen.go @@ -0,0 +1,516 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package gen + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "gorm.io/gen" + "gorm.io/gen/field" + + "gorm.io/plugin/dbresolver" + + customerroles "github.com/OpenIMSDK/OpenKF/server/internal/models/customer_roles" +) + +func newCustomerSlack(db *gorm.DB, opts ...gen.DOOption) customerSlack { + _customerSlack := customerSlack{} + + _customerSlack.customerSlackDo.UseDB(db, opts...) + _customerSlack.customerSlackDo.UseModel(&customerroles.CustomerSlack{}) + + tableName := _customerSlack.customerSlackDo.TableName() + _customerSlack.ALL = field.NewAsterisk(tableName) + _customerSlack.Id = field.NewUint(tableName, "id") + _customerSlack.CreatedAt = field.NewTime(tableName, "created_at") + _customerSlack.UpdatedAt = field.NewTime(tableName, "updated_at") + _customerSlack.DeletedAt = field.NewTime(tableName, "deleted_at") + _customerSlack.UUID = field.NewString(tableName, "uuid") + _customerSlack.Email = field.NewString(tableName, "email") + _customerSlack.Nickname = field.NewString(tableName, "nickname") + _customerSlack.Avatar = field.NewString(tableName, "avatar") + _customerSlack.Description = field.NewString(tableName, "description") + _customerSlack.IsEnable = field.NewBool(tableName, "is_enable") + _customerSlack.FirstName = field.NewString(tableName, "first_name") + _customerSlack.LastName = field.NewString(tableName, "last_name") + _customerSlack.RealName = field.NewString(tableName, "real_name") + _customerSlack.RealNameNormalized = field.NewString(tableName, "real_name_normalized") + _customerSlack.DisplayName = field.NewString(tableName, "display_name") + _customerSlack.DisplayNameNormalized = field.NewString(tableName, "display_name_normalized") + _customerSlack.Skype = field.NewString(tableName, "skype") + _customerSlack.Phone = field.NewString(tableName, "phone") + _customerSlack.Image24 = field.NewString(tableName, "image24") + _customerSlack.Image32 = field.NewString(tableName, "image32") + _customerSlack.Image48 = field.NewString(tableName, "image48") + _customerSlack.Image72 = field.NewString(tableName, "image72") + _customerSlack.Image192 = field.NewString(tableName, "image192") + _customerSlack.Image512 = field.NewString(tableName, "image512") + _customerSlack.ImageOriginal = field.NewString(tableName, "image_original") + _customerSlack.Title = field.NewString(tableName, "title") + _customerSlack.BotID = field.NewString(tableName, "bot_id") + _customerSlack.ApiAppID = field.NewString(tableName, "api_app_id") + _customerSlack.StatusText = field.NewString(tableName, "status_text") + _customerSlack.StatusEmoji = field.NewString(tableName, "status_emoji") + _customerSlack.StatusExpiration = field.NewInt(tableName, "status_expiration") + _customerSlack.Team = field.NewString(tableName, "team") + + _customerSlack.fillFieldMap() + + return _customerSlack +} + +type customerSlack struct { + customerSlackDo customerSlackDo + + ALL field.Asterisk + Id field.Uint + CreatedAt field.Time + UpdatedAt field.Time + DeletedAt field.Time + UUID field.String + Email field.String + Nickname field.String + Avatar field.String + Description field.String + IsEnable field.Bool + FirstName field.String + LastName field.String + RealName field.String + RealNameNormalized field.String + DisplayName field.String + DisplayNameNormalized field.String + Skype field.String + Phone field.String + Image24 field.String + Image32 field.String + Image48 field.String + Image72 field.String + Image192 field.String + Image512 field.String + ImageOriginal field.String + Title field.String + BotID field.String + ApiAppID field.String + StatusText field.String + StatusEmoji field.String + StatusExpiration field.Int + Team field.String + + fieldMap map[string]field.Expr +} + +func (c customerSlack) Table(newTableName string) *customerSlack { + c.customerSlackDo.UseTable(newTableName) + return c.updateTableName(newTableName) +} + +func (c customerSlack) As(alias string) *customerSlack { + c.customerSlackDo.DO = *(c.customerSlackDo.As(alias).(*gen.DO)) + return c.updateTableName(alias) +} + +func (c *customerSlack) updateTableName(table string) *customerSlack { + c.ALL = field.NewAsterisk(table) + c.Id = field.NewUint(table, "id") + c.CreatedAt = field.NewTime(table, "created_at") + c.UpdatedAt = field.NewTime(table, "updated_at") + c.DeletedAt = field.NewTime(table, "deleted_at") + c.UUID = field.NewString(table, "uuid") + c.Email = field.NewString(table, "email") + c.Nickname = field.NewString(table, "nickname") + c.Avatar = field.NewString(table, "avatar") + c.Description = field.NewString(table, "description") + c.IsEnable = field.NewBool(table, "is_enable") + c.FirstName = field.NewString(table, "first_name") + c.LastName = field.NewString(table, "last_name") + c.RealName = field.NewString(table, "real_name") + c.RealNameNormalized = field.NewString(table, "real_name_normalized") + c.DisplayName = field.NewString(table, "display_name") + c.DisplayNameNormalized = field.NewString(table, "display_name_normalized") + c.Skype = field.NewString(table, "skype") + c.Phone = field.NewString(table, "phone") + c.Image24 = field.NewString(table, "image24") + c.Image32 = field.NewString(table, "image32") + c.Image48 = field.NewString(table, "image48") + c.Image72 = field.NewString(table, "image72") + c.Image192 = field.NewString(table, "image192") + c.Image512 = field.NewString(table, "image512") + c.ImageOriginal = field.NewString(table, "image_original") + c.Title = field.NewString(table, "title") + c.BotID = field.NewString(table, "bot_id") + c.ApiAppID = field.NewString(table, "api_app_id") + c.StatusText = field.NewString(table, "status_text") + c.StatusEmoji = field.NewString(table, "status_emoji") + c.StatusExpiration = field.NewInt(table, "status_expiration") + c.Team = field.NewString(table, "team") + + c.fillFieldMap() + + return c +} + +func (c *customerSlack) WithContext(ctx context.Context) ICustomerSlackDo { + return c.customerSlackDo.WithContext(ctx) +} + +func (c customerSlack) TableName() string { return c.customerSlackDo.TableName() } + +func (c customerSlack) Alias() string { return c.customerSlackDo.Alias() } + +func (c *customerSlack) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := c.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (c *customerSlack) fillFieldMap() { + c.fieldMap = make(map[string]field.Expr, 32) + c.fieldMap["id"] = c.Id + c.fieldMap["created_at"] = c.CreatedAt + c.fieldMap["updated_at"] = c.UpdatedAt + c.fieldMap["deleted_at"] = c.DeletedAt + c.fieldMap["uuid"] = c.UUID + c.fieldMap["email"] = c.Email + c.fieldMap["nickname"] = c.Nickname + c.fieldMap["avatar"] = c.Avatar + c.fieldMap["description"] = c.Description + c.fieldMap["is_enable"] = c.IsEnable + c.fieldMap["first_name"] = c.FirstName + c.fieldMap["last_name"] = c.LastName + c.fieldMap["real_name"] = c.RealName + c.fieldMap["real_name_normalized"] = c.RealNameNormalized + c.fieldMap["display_name"] = c.DisplayName + c.fieldMap["display_name_normalized"] = c.DisplayNameNormalized + c.fieldMap["skype"] = c.Skype + c.fieldMap["phone"] = c.Phone + c.fieldMap["image24"] = c.Image24 + c.fieldMap["image32"] = c.Image32 + c.fieldMap["image48"] = c.Image48 + c.fieldMap["image72"] = c.Image72 + c.fieldMap["image192"] = c.Image192 + c.fieldMap["image512"] = c.Image512 + c.fieldMap["image_original"] = c.ImageOriginal + c.fieldMap["title"] = c.Title + c.fieldMap["bot_id"] = c.BotID + c.fieldMap["api_app_id"] = c.ApiAppID + c.fieldMap["status_text"] = c.StatusText + c.fieldMap["status_emoji"] = c.StatusEmoji + c.fieldMap["status_expiration"] = c.StatusExpiration + c.fieldMap["team"] = c.Team +} + +func (c customerSlack) clone(db *gorm.DB) customerSlack { + c.customerSlackDo.ReplaceConnPool(db.Statement.ConnPool) + return c +} + +func (c customerSlack) replaceDB(db *gorm.DB) customerSlack { + c.customerSlackDo.ReplaceDB(db) + return c +} + +type customerSlackDo struct{ gen.DO } + +type ICustomerSlackDo interface { + gen.SubQuery + Debug() ICustomerSlackDo + WithContext(ctx context.Context) ICustomerSlackDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() ICustomerSlackDo + WriteDB() ICustomerSlackDo + As(alias string) gen.Dao + Session(config *gorm.Session) ICustomerSlackDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) ICustomerSlackDo + Not(conds ...gen.Condition) ICustomerSlackDo + Or(conds ...gen.Condition) ICustomerSlackDo + Select(conds ...field.Expr) ICustomerSlackDo + Where(conds ...gen.Condition) ICustomerSlackDo + Order(conds ...field.Expr) ICustomerSlackDo + Distinct(cols ...field.Expr) ICustomerSlackDo + Omit(cols ...field.Expr) ICustomerSlackDo + Join(table schema.Tabler, on ...field.Expr) ICustomerSlackDo + LeftJoin(table schema.Tabler, on ...field.Expr) ICustomerSlackDo + RightJoin(table schema.Tabler, on ...field.Expr) ICustomerSlackDo + Group(cols ...field.Expr) ICustomerSlackDo + Having(conds ...gen.Condition) ICustomerSlackDo + Limit(limit int) ICustomerSlackDo + Offset(offset int) ICustomerSlackDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) ICustomerSlackDo + Unscoped() ICustomerSlackDo + Create(values ...*customerroles.CustomerSlack) error + CreateInBatches(values []*customerroles.CustomerSlack, batchSize int) error + Save(values ...*customerroles.CustomerSlack) error + First() (*customerroles.CustomerSlack, error) + Take() (*customerroles.CustomerSlack, error) + Last() (*customerroles.CustomerSlack, error) + Find() ([]*customerroles.CustomerSlack, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*customerroles.CustomerSlack, err error) + FindInBatches(result *[]*customerroles.CustomerSlack, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*customerroles.CustomerSlack) (info gen.ResultInfo, err error) + Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error) + UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) + Updates(value interface{}) (info gen.ResultInfo, err error) + UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error) + UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) + UpdateColumns(value interface{}) (info gen.ResultInfo, err error) + UpdateFrom(q gen.SubQuery) gen.Dao + Attrs(attrs ...field.AssignExpr) ICustomerSlackDo + Assign(attrs ...field.AssignExpr) ICustomerSlackDo + Joins(fields ...field.RelationField) ICustomerSlackDo + Preload(fields ...field.RelationField) ICustomerSlackDo + FirstOrInit() (*customerroles.CustomerSlack, error) + FirstOrCreate() (*customerroles.CustomerSlack, error) + FindByPage(offset int, limit int) (result []*customerroles.CustomerSlack, count int64, err error) + ScanByPage(result interface{}, offset int, limit int) (count int64, err error) + Scan(result interface{}) (err error) + Returning(value interface{}, columns ...string) ICustomerSlackDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (c customerSlackDo) Debug() ICustomerSlackDo { + return c.withDO(c.DO.Debug()) +} + +func (c customerSlackDo) WithContext(ctx context.Context) ICustomerSlackDo { + return c.withDO(c.DO.WithContext(ctx)) +} + +func (c customerSlackDo) ReadDB() ICustomerSlackDo { + return c.Clauses(dbresolver.Read) +} + +func (c customerSlackDo) WriteDB() ICustomerSlackDo { + return c.Clauses(dbresolver.Write) +} + +func (c customerSlackDo) Session(config *gorm.Session) ICustomerSlackDo { + return c.withDO(c.DO.Session(config)) +} + +func (c customerSlackDo) Clauses(conds ...clause.Expression) ICustomerSlackDo { + return c.withDO(c.DO.Clauses(conds...)) +} + +func (c customerSlackDo) Returning(value interface{}, columns ...string) ICustomerSlackDo { + return c.withDO(c.DO.Returning(value, columns...)) +} + +func (c customerSlackDo) Not(conds ...gen.Condition) ICustomerSlackDo { + return c.withDO(c.DO.Not(conds...)) +} + +func (c customerSlackDo) Or(conds ...gen.Condition) ICustomerSlackDo { + return c.withDO(c.DO.Or(conds...)) +} + +func (c customerSlackDo) Select(conds ...field.Expr) ICustomerSlackDo { + return c.withDO(c.DO.Select(conds...)) +} + +func (c customerSlackDo) Where(conds ...gen.Condition) ICustomerSlackDo { + return c.withDO(c.DO.Where(conds...)) +} + +func (c customerSlackDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) ICustomerSlackDo { + return c.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB())) +} + +func (c customerSlackDo) Order(conds ...field.Expr) ICustomerSlackDo { + return c.withDO(c.DO.Order(conds...)) +} + +func (c customerSlackDo) Distinct(cols ...field.Expr) ICustomerSlackDo { + return c.withDO(c.DO.Distinct(cols...)) +} + +func (c customerSlackDo) Omit(cols ...field.Expr) ICustomerSlackDo { + return c.withDO(c.DO.Omit(cols...)) +} + +func (c customerSlackDo) Join(table schema.Tabler, on ...field.Expr) ICustomerSlackDo { + return c.withDO(c.DO.Join(table, on...)) +} + +func (c customerSlackDo) LeftJoin(table schema.Tabler, on ...field.Expr) ICustomerSlackDo { + return c.withDO(c.DO.LeftJoin(table, on...)) +} + +func (c customerSlackDo) RightJoin(table schema.Tabler, on ...field.Expr) ICustomerSlackDo { + return c.withDO(c.DO.RightJoin(table, on...)) +} + +func (c customerSlackDo) Group(cols ...field.Expr) ICustomerSlackDo { + return c.withDO(c.DO.Group(cols...)) +} + +func (c customerSlackDo) Having(conds ...gen.Condition) ICustomerSlackDo { + return c.withDO(c.DO.Having(conds...)) +} + +func (c customerSlackDo) Limit(limit int) ICustomerSlackDo { + return c.withDO(c.DO.Limit(limit)) +} + +func (c customerSlackDo) Offset(offset int) ICustomerSlackDo { + return c.withDO(c.DO.Offset(offset)) +} + +func (c customerSlackDo) Scopes(funcs ...func(gen.Dao) gen.Dao) ICustomerSlackDo { + return c.withDO(c.DO.Scopes(funcs...)) +} + +func (c customerSlackDo) Unscoped() ICustomerSlackDo { + return c.withDO(c.DO.Unscoped()) +} + +func (c customerSlackDo) Create(values ...*customerroles.CustomerSlack) error { + if len(values) == 0 { + return nil + } + return c.DO.Create(values) +} + +func (c customerSlackDo) CreateInBatches(values []*customerroles.CustomerSlack, batchSize int) error { + return c.DO.CreateInBatches(values, batchSize) +} + +// Save : !!! underlying implementation is different with GORM +// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values) +func (c customerSlackDo) Save(values ...*customerroles.CustomerSlack) error { + if len(values) == 0 { + return nil + } + return c.DO.Save(values) +} + +func (c customerSlackDo) First() (*customerroles.CustomerSlack, error) { + if result, err := c.DO.First(); err != nil { + return nil, err + } else { + return result.(*customerroles.CustomerSlack), nil + } +} + +func (c customerSlackDo) Take() (*customerroles.CustomerSlack, error) { + if result, err := c.DO.Take(); err != nil { + return nil, err + } else { + return result.(*customerroles.CustomerSlack), nil + } +} + +func (c customerSlackDo) Last() (*customerroles.CustomerSlack, error) { + if result, err := c.DO.Last(); err != nil { + return nil, err + } else { + return result.(*customerroles.CustomerSlack), nil + } +} + +func (c customerSlackDo) Find() ([]*customerroles.CustomerSlack, error) { + result, err := c.DO.Find() + return result.([]*customerroles.CustomerSlack), err +} + +func (c customerSlackDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*customerroles.CustomerSlack, err error) { + buf := make([]*customerroles.CustomerSlack, 0, batchSize) + err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { + defer func() { results = append(results, buf...) }() + return fc(tx, batch) + }) + return results, err +} + +func (c customerSlackDo) FindInBatches(result *[]*customerroles.CustomerSlack, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return c.DO.FindInBatches(result, batchSize, fc) +} + +func (c customerSlackDo) Attrs(attrs ...field.AssignExpr) ICustomerSlackDo { + return c.withDO(c.DO.Attrs(attrs...)) +} + +func (c customerSlackDo) Assign(attrs ...field.AssignExpr) ICustomerSlackDo { + return c.withDO(c.DO.Assign(attrs...)) +} + +func (c customerSlackDo) Joins(fields ...field.RelationField) ICustomerSlackDo { + for _, _f := range fields { + c = *c.withDO(c.DO.Joins(_f)) + } + return &c +} + +func (c customerSlackDo) Preload(fields ...field.RelationField) ICustomerSlackDo { + for _, _f := range fields { + c = *c.withDO(c.DO.Preload(_f)) + } + return &c +} + +func (c customerSlackDo) FirstOrInit() (*customerroles.CustomerSlack, error) { + if result, err := c.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*customerroles.CustomerSlack), nil + } +} + +func (c customerSlackDo) FirstOrCreate() (*customerroles.CustomerSlack, error) { + if result, err := c.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*customerroles.CustomerSlack), nil + } +} + +func (c customerSlackDo) FindByPage(offset int, limit int) (result []*customerroles.CustomerSlack, count int64, err error) { + result, err = c.Offset(offset).Limit(limit).Find() + if err != nil { + return + } + + if size := len(result); 0 < limit && 0 < size && size < limit { + count = int64(size + offset) + return + } + + count, err = c.Offset(-1).Limit(-1).Count() + return +} + +func (c customerSlackDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = c.Count() + if err != nil { + return + } + + err = c.Offset(offset).Limit(limit).Scan(result) + return +} + +func (c customerSlackDo) Scan(result interface{}) (err error) { + return c.DO.Scan(result) +} + +func (c customerSlackDo) Delete(models ...*customerroles.CustomerSlack) (result gen.ResultInfo, err error) { + return c.DO.Delete(models) +} + +func (c *customerSlackDo) withDO(do gen.Dao) *customerSlackDo { + c.DO = *do.(*gen.DO) + return c +} diff --git a/server/internal/dal/gen/gen.go b/server/internal/dal/gen/gen.go index fef77f7..00405fb 100644 --- a/server/internal/dal/gen/gen.go +++ b/server/internal/dal/gen/gen.go @@ -16,15 +16,17 @@ import ( ) var ( - Q = new(Query) - SysBot *sysBot - SysCommunity *sysCommunity - SysCustomer *sysCustomer - SysUser *sysUser + Q = new(Query) + CustomerSlack *customerSlack + SysBot *sysBot + SysCommunity *sysCommunity + SysCustomer *sysCustomer + SysUser *sysUser ) func SetDefault(db *gorm.DB, opts ...gen.DOOption) { *Q = *Use(db, opts...) + CustomerSlack = &Q.CustomerSlack SysBot = &Q.SysBot SysCommunity = &Q.SysCommunity SysCustomer = &Q.SysCustomer @@ -33,32 +35,35 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) { func Use(db *gorm.DB, opts ...gen.DOOption) *Query { return &Query{ - db: db, - SysBot: newSysBot(db, opts...), - SysCommunity: newSysCommunity(db, opts...), - SysCustomer: newSysCustomer(db, opts...), - SysUser: newSysUser(db, opts...), + db: db, + CustomerSlack: newCustomerSlack(db, opts...), + SysBot: newSysBot(db, opts...), + SysCommunity: newSysCommunity(db, opts...), + SysCustomer: newSysCustomer(db, opts...), + SysUser: newSysUser(db, opts...), } } type Query struct { db *gorm.DB - SysBot sysBot - SysCommunity sysCommunity - SysCustomer sysCustomer - SysUser sysUser + CustomerSlack customerSlack + SysBot sysBot + SysCommunity sysCommunity + SysCustomer sysCustomer + SysUser sysUser } func (q *Query) Available() bool { return q.db != nil } func (q *Query) clone(db *gorm.DB) *Query { return &Query{ - db: db, - SysBot: q.SysBot.clone(db), - SysCommunity: q.SysCommunity.clone(db), - SysCustomer: q.SysCustomer.clone(db), - SysUser: q.SysUser.clone(db), + db: db, + CustomerSlack: q.CustomerSlack.clone(db), + SysBot: q.SysBot.clone(db), + SysCommunity: q.SysCommunity.clone(db), + SysCustomer: q.SysCustomer.clone(db), + SysUser: q.SysUser.clone(db), } } @@ -72,27 +77,30 @@ func (q *Query) WriteDB() *Query { func (q *Query) ReplaceDB(db *gorm.DB) *Query { return &Query{ - db: db, - SysBot: q.SysBot.replaceDB(db), - SysCommunity: q.SysCommunity.replaceDB(db), - SysCustomer: q.SysCustomer.replaceDB(db), - SysUser: q.SysUser.replaceDB(db), + db: db, + CustomerSlack: q.CustomerSlack.replaceDB(db), + SysBot: q.SysBot.replaceDB(db), + SysCommunity: q.SysCommunity.replaceDB(db), + SysCustomer: q.SysCustomer.replaceDB(db), + SysUser: q.SysUser.replaceDB(db), } } type queryCtx struct { - SysBot ISysBotDo - SysCommunity ISysCommunityDo - SysCustomer ISysCustomerDo - SysUser ISysUserDo + CustomerSlack ICustomerSlackDo + SysBot ISysBotDo + SysCommunity ISysCommunityDo + SysCustomer ISysCustomerDo + SysUser ISysUserDo } func (q *Query) WithContext(ctx context.Context) *queryCtx { return &queryCtx{ - SysBot: q.SysBot.WithContext(ctx), - SysCommunity: q.SysCommunity.WithContext(ctx), - SysCustomer: q.SysCustomer.WithContext(ctx), - SysUser: q.SysUser.WithContext(ctx), + CustomerSlack: q.CustomerSlack.WithContext(ctx), + SysBot: q.SysBot.WithContext(ctx), + SysCommunity: q.SysCommunity.WithContext(ctx), + SysCustomer: q.SysCustomer.WithContext(ctx), + SysUser: q.SysUser.WithContext(ctx), } } diff --git a/server/internal/models/customer_roles/customer_slack.go b/server/internal/models/customer_roles/customer_slack.go new file mode 100644 index 0000000..6a75801 --- /dev/null +++ b/server/internal/models/customer_roles/customer_slack.go @@ -0,0 +1,50 @@ +// Copyright © 2023 OpenIM open source community. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package customerroles + +import "github.com/OpenIMSDK/OpenKF/server/internal/models/base" + +// CustomerSlack customer from slack model. +type CustomerSlack struct { + base.UserBase // UserID in uuid => type+userid. example: slackU05BL9CAN7N + + FirstName string `json:"first_name" gorm:"column:first_name;type:varchar(256);comment:'First name'"` + LastName string `json:"last_name" gorm:"column:last_name;type:varchar(256);comment:'Last name'"` + RealName string `json:"real_name" gorm:"column:real_name;type:varchar(256);comment:'Real name'"` + RealNameNormalized string `json:"real_name_normalized" gorm:"column:real_name_normalized;type:varchar(256);comment:'Real name normalized'"` + DisplayName string `json:"display_name" gorm:"column:display_name;type:varchar(256);comment:'Display name'"` + DisplayNameNormalized string `json:"display_name_normalized" gorm:"column:display_name_normalized;type:varchar(256);comment:'Display name normalized'"` + Skype string `json:"skype" gorm:"column:skype;type:varchar(256);comment:'Skype'"` + Phone string `json:"phone" gorm:"column:phone;type:varchar(256);comment:'Phone'"` + Image24 string `json:"image_24" gorm:"column:image_24;type:varchar(256);comment:'Image 24'"` + Image32 string `json:"image_32" gorm:"column:image_32;type:varchar(256);comment:'Image 32'"` + Image48 string `json:"image_48" gorm:"column:image_48;type:varchar(256);comment:'Image 48'"` + Image72 string `json:"image_72" gorm:"column:image_72;type:varchar(256);comment:'Image 72'"` + Image192 string `json:"image_192" gorm:"column:image_192;type:varchar(256);comment:'Image 192'"` + Image512 string `json:"image_512" gorm:"column:image_512;type:varchar(256);comment:'Image 512'"` + ImageOriginal string `json:"image_original" gorm:"column:image_original;type:varchar(256);comment:'Image original'"` + Title string `json:"title" gorm:"column:title;type:varchar(256);comment:'Title'"` + BotID string `json:"bot_id,omitempty" gorm:"column:bot_id;type:varchar(256);comment:'Bot id'"` + ApiAppID string `json:"api_app_id,omitempty" gorm:"column:api_app_id;type:varchar(256);comment:'Api app id'"` + StatusText string `json:"status_text,omitempty" gorm:"column:status_text;type:varchar(256);comment:'Status text'"` + StatusEmoji string `json:"status_emoji,omitempty" gorm:"column:status_emoji;type:varchar(256);comment:'Status emoji'"` + StatusExpiration int `json:"status_expiration" gorm:"column:status_expiration;type:int(11);comment:'Status expiration'"` + Team string `json:"team" gorm:"column:team;type:varchar(256);comment:'Team'"` +} + +// TableName table name. +func (CustomerSlack) TableName() string { + return "customer_slack" +} diff --git a/server/internal/msg/slack_cmd/slack.go b/server/internal/msg/slack_cmd/slack.go new file mode 100644 index 0000000..d583d6a --- /dev/null +++ b/server/internal/msg/slack_cmd/slack.go @@ -0,0 +1,64 @@ +// Copyright © 2023 OpenIM open source community. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package slackcmd + +import ( + "context" + "strings" + + "github.com/OpenIMSDK/OpenKF/server/internal/config" + "github.com/OpenIMSDK/OpenKF/server/internal/service" + "github.com/OpenIMSDK/OpenKF/server/pkg/log" + "github.com/shomali11/slacker" +) + +func InitSlack() { + bot_token := config.Config.Slack.BotToken + app_token := config.Config.Slack.AppToken + debug := config.Config.App.Debug + + svc := service.NewSlackService(context.Background()) + + bot := slacker.NewClient(bot_token, app_token, slacker.WithDebug(debug)) + + // receive all @ message + bot.Command("", &slacker.CommandDefinition{ + Handler: func(bc slacker.BotContext, r slacker.Request, w slacker.ResponseWriter) { + // find or create a customer + messageEvent := bc.Event() + senderId, _, err := svc.CreateCustomer(messageEvent.UserID, messageEvent.UserProfile) + if err != nil { + log.Errorf("Slack", "CreateCustomer failed: %s", err.Error()) + return + } + + question := r.Param("question") + // todo: remove id from question + words := strings.Split(question, " ") + question = strings.Join(words[1:], " ") + + _ = senderId + w.Reply("senderId", slacker.WithThreadReply(true)) + }, + }) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + err := bot.Listen(ctx) + if err != nil { + log.Panicf("Slack Client", "Connection failed: %s", err.Error()) + } +} diff --git a/server/internal/msg/staff_dispatch/dispatch.go b/server/internal/msg/staff_dispatch/dispatch.go new file mode 100644 index 0000000..574b2c7 --- /dev/null +++ b/server/internal/msg/staff_dispatch/dispatch.go @@ -0,0 +1,15 @@ +// Copyright © 2023 OpenIM open source community. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package staffdispatch diff --git a/server/internal/service/slack.go b/server/internal/service/slack.go index 1ac56d2..7d0b324 100644 --- a/server/internal/service/slack.go +++ b/server/internal/service/slack.go @@ -15,22 +15,36 @@ package service import ( + "context" + "fmt" + "github.com/OpenIMSDK/OpenKF/server/internal/config" + "github.com/OpenIMSDK/OpenKF/server/internal/dal/dao" + "github.com/OpenIMSDK/OpenKF/server/internal/models/base" + customerroles "github.com/OpenIMSDK/OpenKF/server/internal/models/customer_roles" responseparams "github.com/OpenIMSDK/OpenKF/server/internal/params/response" - "github.com/gin-gonic/gin" + "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request" + "github.com/OpenIMSDK/OpenKF/server/pkg/utils" + "github.com/slack-go/slack" ) +// SLACK_PERFIX do not use separator +const SLACK_PERFIX = "slack" + // SlackService bot service. type SlackService struct { Service + + CustomerSlackDao *dao.CustomerSlackDao } -// NewSlackService return new service with gin context. -func NewSlackService(c *gin.Context) *SlackService { +// NewSlackService return new service with Background context. +func NewSlackService(ctx context.Context) *SlackService { return &SlackService{ Service: Service{ - ctx: c, + ctx: ctx, }, + CustomerSlackDao: dao.NewCustomerSlackDao(), } } @@ -46,3 +60,76 @@ func (svc *SlackService) GetSlackConfig() (*responseparams.SlackConfigResponse, VerificationToken: config.Config.Slack.VerificationToken, }, nil } + +// CreateCustomer create customer if not exists. +func (svc *SlackService) CreateCustomer(userId string, profile *slack.UserProfile) (string, uint, error) { + // check if exists + temp, err := svc.CustomerSlackDao.FindFirstByUUID(userId) + if temp != nil || err == nil { + return temp.UUID, temp.Id, err + } + + // Set unique email + if profile.Email == "" { + profile.Email = fmt.Sprintf("%s %s", "NOEMAIL", utils.GenUUIDWithoutHyphen()) + } + + customerSlack := customerroles.CustomerSlack{ + UserBase: base.UserBase{ + // UUID: fmt.Sprintf("%s%s", SLACK_PERFIX, userId), + UUID: userId, + Email: profile.Email, + Nickname: profile.FirstName, + Avatar: profile.Image512, + Description: profile.Title, + IsEnable: true, + }, + FirstName: profile.FirstName, + LastName: profile.LastName, + RealName: profile.RealName, + RealNameNormalized: profile.RealNameNormalized, + DisplayName: profile.DisplayName, + DisplayNameNormalized: profile.DisplayNameNormalized, + Skype: profile.Skype, + Phone: profile.Phone, + Image24: profile.Image24, + Image32: profile.Image32, + Image48: profile.Image48, + Image72: profile.Image72, + Image192: profile.Image192, + Image512: profile.Image512, + ImageOriginal: profile.ImageOriginal, + Title: profile.Title, + BotID: profile.BotID, + ApiAppID: profile.ApiAppID, + StatusText: profile.StatusText, + StatusEmoji: profile.StatusEmoji, + StatusExpiration: profile.StatusExpiration, + Team: profile.Team, + } + if err := svc.CustomerSlackDao.Create(&customerSlack); err != nil { + return customerSlack.UUID, 0, err + } + + s, _ := svc.CustomerSlackDao.FindFirstByUUID(userId) + + param := &request.RegisterUserParams{ + Secret: config.Config.OpenIM.Secret, + Users: []request.User{ + { + UserID: userId, + Nickname: customerSlack.Nickname, + FaceURL: "", // Use OpenKF avatar + }, + }, + } + ok, err := registerUserToOpenIM(param) + if err != nil || !ok { + // Assume that the user has been created/deleted successfully + _ = svc.CustomerSlackDao.Delete(s) + + return userId, s.Id, err + } + + return userId, s.Id, nil +} diff --git a/server/main.go b/server/main.go index ed6b089..c7d3923 100644 --- a/server/main.go +++ b/server/main.go @@ -22,6 +22,7 @@ import ( "github.com/OpenIMSDK/OpenKF/server/internal/conn/client" "github.com/OpenIMSDK/OpenKF/server/internal/conn/db" "github.com/OpenIMSDK/OpenKF/server/internal/middleware/hooks" + slackcmd "github.com/OpenIMSDK/OpenKF/server/internal/msg/slack_cmd" "github.com/OpenIMSDK/OpenKF/server/internal/router" "github.com/OpenIMSDK/OpenKF/server/internal/utils" "github.com/OpenIMSDK/OpenKF/server/pkg/log" @@ -42,6 +43,7 @@ func init() { client.InitMinio() // client.InitMail() hooks.InitHooks() + slackcmd.InitSlack() } //go:generate go env -w GO111MODULE=on From 6d040740c1e86d78f4f1f075699cdb9038ebb98c Mon Sep 17 00:00:00 2001 From: IRONICBo <47499836+IRONICBo@users.noreply.github.com> Date: Fri, 11 Aug 2023 23:26:27 +0800 Subject: [PATCH 2/6] fix: Update lint. Signed-off-by: IRONICBo <47499836+IRONICBo@users.noreply.github.com> --- server/internal/msg/slack_cmd/slack.go | 17 ++++++++++------- server/internal/service/slack.go | 7 ++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/server/internal/msg/slack_cmd/slack.go b/server/internal/msg/slack_cmd/slack.go index d583d6a..afa6d5a 100644 --- a/server/internal/msg/slack_cmd/slack.go +++ b/server/internal/msg/slack_cmd/slack.go @@ -18,20 +18,22 @@ import ( "context" "strings" + "github.com/shomali11/slacker" + "github.com/OpenIMSDK/OpenKF/server/internal/config" "github.com/OpenIMSDK/OpenKF/server/internal/service" "github.com/OpenIMSDK/OpenKF/server/pkg/log" - "github.com/shomali11/slacker" ) +// InitSlack init slack client. func InitSlack() { - bot_token := config.Config.Slack.BotToken - app_token := config.Config.Slack.AppToken + botToken := config.Config.Slack.BotToken + appToken := config.Config.Slack.AppToken debug := config.Config.App.Debug svc := service.NewSlackService(context.Background()) - bot := slacker.NewClient(bot_token, app_token, slacker.WithDebug(debug)) + bot := slacker.NewClient(botToken, appToken, slacker.WithDebug(debug)) // receive all @ message bot.Command("", &slacker.CommandDefinition{ @@ -41,6 +43,7 @@ func InitSlack() { senderId, _, err := svc.CreateCustomer(messageEvent.UserID, messageEvent.UserProfile) if err != nil { log.Errorf("Slack", "CreateCustomer failed: %s", err.Error()) + return } @@ -50,15 +53,15 @@ func InitSlack() { question = strings.Join(words[1:], " ") _ = senderId - w.Reply("senderId", slacker.WithThreadReply(true)) + _ = question + _ = w.Reply("senderId", slacker.WithThreadReply(true)) }, }) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - err := bot.Listen(ctx) - if err != nil { + if err := bot.Listen(ctx); err != nil { log.Panicf("Slack Client", "Connection failed: %s", err.Error()) } } diff --git a/server/internal/service/slack.go b/server/internal/service/slack.go index 7d0b324..c21de55 100644 --- a/server/internal/service/slack.go +++ b/server/internal/service/slack.go @@ -18,6 +18,8 @@ import ( "context" "fmt" + "github.com/slack-go/slack" + "github.com/OpenIMSDK/OpenKF/server/internal/config" "github.com/OpenIMSDK/OpenKF/server/internal/dal/dao" "github.com/OpenIMSDK/OpenKF/server/internal/models/base" @@ -25,10 +27,9 @@ import ( responseparams "github.com/OpenIMSDK/OpenKF/server/internal/params/response" "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request" "github.com/OpenIMSDK/OpenKF/server/pkg/utils" - "github.com/slack-go/slack" ) -// SLACK_PERFIX do not use separator +// SLACK_PERFIX do not use separator. const SLACK_PERFIX = "slack" // SlackService bot service. @@ -107,7 +108,7 @@ func (svc *SlackService) CreateCustomer(userId string, profile *slack.UserProfil StatusExpiration: profile.StatusExpiration, Team: profile.Team, } - if err := svc.CustomerSlackDao.Create(&customerSlack); err != nil { + if err = svc.CustomerSlackDao.Create(&customerSlack); err != nil { return customerSlack.UUID, 0, err } From 73168b73024f0599222c7f4e1959cee61c858e39 Mon Sep 17 00:00:00 2001 From: IRONICBo <47499836+IRONICBo@users.noreply.github.com> Date: Sun, 13 Aug 2023 11:47:46 +0800 Subject: [PATCH 3/6] fix: Split modules to client and cmd. Signed-off-by: IRONICBo <47499836+IRONICBo@users.noreply.github.com> --- server/internal/conn/client/slack.go | 37 ++++++++++++++++++++++++++ server/internal/msg/slack_cmd/slack.go | 15 +++++------ server/main.go | 2 +- 3 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 server/internal/conn/client/slack.go diff --git a/server/internal/conn/client/slack.go b/server/internal/conn/client/slack.go new file mode 100644 index 0000000..1bf15a2 --- /dev/null +++ b/server/internal/conn/client/slack.go @@ -0,0 +1,37 @@ +// Copyright © 2023 OpenIM open source community. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package client + +import ( + "github.com/shomali11/slacker" + + "github.com/OpenIMSDK/OpenKF/server/internal/config" + "github.com/OpenIMSDK/OpenKF/server/pkg/log" +) + +// SlackBot slack client. +var SlackBot *slacker.Slacker + +// InitSlack init slack client. +func InitSlack() *slacker.Slacker { + botToken := config.Config.Slack.BotToken + appToken := config.Config.Slack.AppToken + debug := config.Config.App.Debug + + SlackBot = slacker.NewClient(botToken, appToken, slacker.WithDebug(debug)) + log.Info("Slack", "init config ok") + + return SlackBot +} diff --git a/server/internal/msg/slack_cmd/slack.go b/server/internal/msg/slack_cmd/slack.go index afa6d5a..b276f50 100644 --- a/server/internal/msg/slack_cmd/slack.go +++ b/server/internal/msg/slack_cmd/slack.go @@ -20,21 +20,17 @@ import ( "github.com/shomali11/slacker" - "github.com/OpenIMSDK/OpenKF/server/internal/config" + "github.com/OpenIMSDK/OpenKF/server/internal/conn/client" "github.com/OpenIMSDK/OpenKF/server/internal/service" "github.com/OpenIMSDK/OpenKF/server/pkg/log" ) -// InitSlack init slack client. -func InitSlack() { - botToken := config.Config.Slack.BotToken - appToken := config.Config.Slack.AppToken - debug := config.Config.App.Debug +// InitSlackListen init slack socket listen. +func InitSlackListen() { + bot := client.InitSlack() svc := service.NewSlackService(context.Background()) - bot := slacker.NewClient(botToken, appToken, slacker.WithDebug(debug)) - // receive all @ message bot.Command("", &slacker.CommandDefinition{ Handler: func(bc slacker.BotContext, r slacker.Request, w slacker.ResponseWriter) { @@ -48,10 +44,11 @@ func InitSlack() { } question := r.Param("question") - // todo: remove id from question + // TODO: remove id from question words := strings.Split(question, " ") question = strings.Join(words[1:], " ") + // TODO: get staff id and get into im process. _ = senderId _ = question _ = w.Reply("senderId", slacker.WithThreadReply(true)) diff --git a/server/main.go b/server/main.go index c7d3923..a01b745 100644 --- a/server/main.go +++ b/server/main.go @@ -43,7 +43,7 @@ func init() { client.InitMinio() // client.InitMail() hooks.InitHooks() - slackcmd.InitSlack() + slackcmd.InitSlackListen() } //go:generate go env -w GO111MODULE=on From 0d20f98cca099ab4cbc4281da87a85e2a1f5ef4a Mon Sep 17 00:00:00 2001 From: IRONICBo <47499836+IRONICBo@users.noreply.github.com> Date: Sun, 13 Aug 2023 18:42:49 +0800 Subject: [PATCH 4/6] feat: Add user dispatch with redis zset. Signed-off-by: IRONICBo <47499836+IRONICBo@users.noreply.github.com> --- server/internal/api/callback.go | 2 + server/internal/api/user.go | 39 +++- server/internal/dal/cache/redis.go | 159 ++++++++++++++++ server/internal/dal/dao/user_dispatch_dao.go | 169 ++++++++++++++++++ .../internal/middleware/hooks/gen_jwt_hook.go | 1 + server/internal/router/router.go | 1 + server/internal/service/user_dispatch.go | 61 +++++++ server/internal/service/user_dispatch_test.go | 44 +++++ server/main.go | 4 +- web/src/api/index/login.ts | 8 + .../layouts/components/LayoutSideNav.vue | 4 +- 11 files changed, 489 insertions(+), 3 deletions(-) create mode 100644 server/internal/dal/dao/user_dispatch_dao.go create mode 100644 server/internal/service/user_dispatch.go create mode 100644 server/internal/service/user_dispatch_test.go diff --git a/server/internal/api/callback.go b/server/internal/api/callback.go index c4424a8..a71ead3 100644 --- a/server/internal/api/callback.go +++ b/server/internal/api/callback.go @@ -62,10 +62,12 @@ func MsgModify(c *gin.Context) { // UserOnline. func UserOnline(c *gin.Context) { + // TODO: push user id to queue } // UserOffline. func UserOffline(c *gin.Context) { + // TODO: remove user id to queue } // OfflinePush. diff --git a/server/internal/api/user.go b/server/internal/api/user.go index 5b0c2e7..fcecae1 100644 --- a/server/internal/api/user.go +++ b/server/internal/api/user.go @@ -74,7 +74,16 @@ func AccountLogin(c *gin.Context) { svc := service.NewUserService(c) u, err := svc.LoginWithAccount(¶ms) if err != nil { - log.Debug("AdminRegister error", err) + log.Debug("Login error", err) + response.FailWithCode(common.ERROR, c) + + return + } + + // TODO: Add login status to user dispatch + dsvc := service.NewUserDispatchService(c.Request.Context()) + if err = dsvc.AddUser(u.UUID); err != nil { + log.Error("Add to online status error", err) response.FailWithCode(common.ERROR, c) return @@ -83,6 +92,34 @@ func AccountLogin(c *gin.Context) { response.SuccessWithData(u, c) } +// AccountExit +// @Tags user +// @Summary AccountExit +// @Description logout with account +// @Produce application/json +// @Security ApiKeyAuth +// @Success 200 {object} response.Response{msg=string} "Success" +// @Router /api/v1/login/account [post]. +func AccountExit(c *gin.Context) { + uuid, err := utils.GetUserUUID(c) + if err != nil { + response.FailWithCode(common.UNAUTHORIZED, c) + + return + } + + svc := service.NewUserDispatchService(c) + err = svc.DeleteUser(uuid) + if err != nil { + log.Error("Delete user from online status error", err) + response.FailWithCode(common.ERROR, c) + + return + } + + response.Success(c) +} + // GetUserInfo // @Tags user // @Summary GetUserInfo diff --git a/server/internal/dal/cache/redis.go b/server/internal/dal/cache/redis.go index 95312b3..0a28593 100644 --- a/server/internal/dal/cache/redis.go +++ b/server/internal/dal/cache/redis.go @@ -26,6 +26,7 @@ var _ Cache = new(cache) // Cache cache interface. type Cache interface { + // default Set(ctx context.Context, key, value string, ttl time.Duration) error Get(ctx context.Context, key string) (string, error) TTL(ctx context.Context, key string) (time.Duration, error) @@ -35,6 +36,29 @@ type Cache interface { Exists(ctx context.Context, keys ...string) bool Incr(ctx context.Context, key string) int64 Close() error + + // List + LPush(ctx context.Context, key string, values ...interface{}) error + RPush(ctx context.Context, key string, values ...interface{}) error + LPop(ctx context.Context, key string) (string, error) + RPop(ctx context.Context, key string) (string, error) + LRange(ctx context.Context, key string, start, stop int64) ([]string, error) + LRem(ctx context.Context, key string, count int64, value interface{}) (int64, error) + LTrim(ctx context.Context, key string, start, stop int64) error + LLen(ctx context.Context, key string) (int64, error) + + // ZSet + ZAdd(ctx context.Context, key string, members ...*redis.Z) (int64, error) + ZRem(ctx context.Context, key string, members ...interface{}) (int64, error) + ZRange(ctx context.Context, key string, start, stop int64) ([]string, error) + + // tx + Pipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error) + Pipline() redis.Pipeliner + TxPipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error) + TxPipeline() redis.Pipeliner + + Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) error } // cache cache dao representative. @@ -124,3 +148,138 @@ func (c *cache) Incr(ctx context.Context, key string) int64 { func (c *cache) Close() error { return c.client.Close() } + +// LPush left push value to list. +func (c *cache) LPush(ctx context.Context, key string, values ...interface{}) error { + return c.client.LPush(ctx, key, values...).Err() +} + +// RPush right push value to list. +func (c *cache) RPush(ctx context.Context, key string, values ...interface{}) error { + return c.client.RPush(ctx, key, values...).Err() +} + +// LPop left pop value from list. +func (c *cache) LPop(ctx context.Context, key string) (string, error) { + return c.client.LPop(ctx, key).Result() +} + +// RPop right pop value from list. +func (c *cache) RPop(ctx context.Context, key string) (string, error) { + return c.client.RPop(ctx, key).Result() +} + +// LRange get list value from start to stop. +func (c *cache) LRange(ctx context.Context, key string, start, stop int64) ([]string, error) { + return c.client.LRange(ctx, key, start, stop).Result() +} + +// LRem remove count value from list. +func (c *cache) LRem(ctx context.Context, key string, count int64, value interface{}) (int64, error) { + return c.client.LRem(ctx, key, count, value).Result() +} + +// LTrim trim list from start to stop. +func (c *cache) LTrim(ctx context.Context, key string, start, stop int64) error { + return c.client.LTrim(ctx, key, start, stop).Err() +} + +// LLen get list length. +func (c *cache) LLen(ctx context.Context, key string) (int64, error) { + return c.client.LLen(ctx, key).Result() +} + +// Pipelined pipelined. +func (c *cache) Pipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error) { + return c.client.Pipelined(ctx, fn) +} + +// Pipline pipline. +func (c *cache) Pipline() redis.Pipeliner { + return c.client.Pipeline() +} + +// TxPipelined tx pipelined. +func (c *cache) TxPipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error) { + return c.client.TxPipelined(ctx, fn) +} + +// TxPipeline tx pipeline. +func (c *cache) TxPipeline() redis.Pipeliner { + return c.client.TxPipeline() +} + +// Watch watch. +func (c *cache) Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) error { + return c.client.Watch(ctx, fn, keys...) +} + +// LPopAndRPush lpop and rpush. +func (c *cache) LPopAndRPush(ctx context.Context, key string) (string, error) { + tx := c.client.TxPipeline() + defer tx.Close() + + // check length + length, err := c.client.LLen(ctx, key).Result() + if err != nil || length <= 1 { + return "", errors.Wrapf(err, "list is not exists or length less than 1: %s err", key) + } + + // get head value + headVal, _ := c.client.LIndex(ctx, key, 0).Result() + + _ = tx.Process(ctx, c.client.LPop(ctx, key)) + _ = tx.Process(ctx, c.client.RPush(ctx, key, headVal)) + + _, err = tx.Exec(ctx) + if err != nil { + return "", err + } + + return headVal, nil +} + +// ZAdd zset add. +func (c *cache) ZAdd(ctx context.Context, key string, members ...*redis.Z) (int64, error) { + return c.client.ZAdd(ctx, key, members...).Result() +} + +// ZRem zset remove. +func (c *cache) ZRem(ctx context.Context, key string, members ...interface{}) (int64, error) { + return c.client.ZRem(ctx, key, members...).Result() +} + +// ZRange zset range. +func (c *cache) ZRange(ctx context.Context, key string, start, stop int64) ([]string, error) { + return c.client.ZRange(ctx, key, start, stop).Result() +} + +// // EnqueueStringLPush enqueue key and push left. +// func (c *cache) EnqueueStringLPush(ctx context.Context, key, value string) error { +// c.mu.Lock() +// defer c.mu.Unlock() + +// err := c.client.LPush(ctx, key, value).Err() +// if err != nil { +// return errors.Wrapf(err, "redis enqueue key: %s , value: %s, err: %s", key, value, err.Error()) +// } + +// return nil +// } + +// // DequeueStringRPop dequeue key and pop right. +// func (c *cache) DequeueStringRPop(ctx context.Context, key string) (string, error) { +// c.mu.Lock() +// defer c.mu.Unlock() + +// value, err := c.client.RPop(ctx, key).Result() +// if err != nil { +// return "", errors.Wrapf(err, "redis dequeue key: %s , err: %s", key, err.Error()) +// } + +// return value, nil +// } + +// func (c *cache) TxPipeline(ctx context.Context) redis.Pipeliner { +// return c.client.TxPipeline() +// } diff --git a/server/internal/dal/dao/user_dispatch_dao.go b/server/internal/dal/dao/user_dispatch_dao.go new file mode 100644 index 0000000..9cba8de --- /dev/null +++ b/server/internal/dal/dao/user_dispatch_dao.go @@ -0,0 +1,169 @@ +// Copyright © 2023 OpenIM open source community. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dao + +import ( + "context" + "sync" + "time" + + "github.com/go-redis/redis/v8" + "github.com/pkg/errors" + + "github.com/OpenIMSDK/OpenKF/server/internal/conn/db" + "github.com/OpenIMSDK/OpenKF/server/internal/dal/cache" + "github.com/OpenIMSDK/OpenKF/server/internal/dal/gen" +) + +// UserDispatchDao user dispatch dao. +type UserDispatchDao struct { + Dao + + mu sync.Mutex +} + +// NewUserDispatchDao return a user dispatch dao. +func NewUserDispatchDao() *UserDispatchDao { + query := gen.Use(db.GetMysqlDB()) + cache := cache.Use(db.GetRedis()) + + return &UserDispatchDao{ + Dao: Dao{ + ctx: context.Background(), + query: query, + cache: &cache, + }, + + mu: sync.Mutex{}, + } +} + +// EnqueueStringLPush enqueue string left push. +func (d *UserDispatchDao) EnqueueStringLPush(key, value string) error { + d.mu.Lock() + defer d.mu.Unlock() + + err := (*d.cache).LPush(d.ctx, key, value) + if err != nil { + return errors.Wrapf(err, "enqueue key: %s , value: %s, err: %s", key, value, err.Error()) + } + + return nil +} + +// EnqueueStringRPush enqueue string right push. +func (d *UserDispatchDao) EnqueueStringRPush(key, value string) error { + d.mu.Lock() + defer d.mu.Unlock() + + err := (*d.cache).RPush(d.ctx, key, value) + if err != nil { + return errors.Wrapf(err, "enqueue key: %s , value: %s, err: %s", key, value, err.Error()) + } + + return nil +} + +// DequeueStringRPop dequeue string right pop. +func (d *UserDispatchDao) DequeueStringRPop(key string) (string, error) { + d.mu.Lock() + defer d.mu.Unlock() + + value, err := (*d.cache).RPop(d.ctx, key) + if err != nil { + return "", errors.Wrapf(err, "dequeue key: %s , err: %s", key, err.Error()) + } + + return value, nil +} + +// DequeueStringLPop dequeue string right pop. +func (d *UserDispatchDao) DequeueStringLPop(key string) (string, error) { + d.mu.Lock() + defer d.mu.Unlock() + + value, err := (*d.cache).LPop(d.ctx, key) + if err != nil { + return "", errors.Wrapf(err, "dequeue key: %s , err: %s", key, err.Error()) + } + + return value, nil +} + +// QueueLen get queue length. +func (d *UserDispatchDao) QueueLen(key string) (int64, error) { + d.mu.Lock() + defer d.mu.Unlock() + + length, err := (*d.cache).LLen(d.ctx, key) + if err != nil { + return 0, errors.Wrapf(err, "enqueue key: %s , err: %s", key, err.Error()) + } + + return length, nil +} + +// DequeueWithEnqueue dequeue with enqueue. +func (d *UserDispatchDao) DequeueWithEnqueue(key string) (string, error) { + d.mu.Lock() + defer d.mu.Unlock() + + value, err := (*d.cache).RPop(d.ctx, key) + if err != nil { + return "", errors.Wrapf(err, "dequeue with enqueue: %s , err: %s", key, err.Error()) + } + + err = (*d.cache).LPush(d.ctx, key, value) + if err != nil { + return "", errors.Wrapf(err, "dequeue with enqueue: %s , err: %s", key, err.Error()) + } + + return value, nil +} + +// AddUser add user to zset. +func (d *UserDispatchDao) AddUser(key, value string) (int64, error) { + res, err := (*d.cache).ZAdd(d.ctx, key, &redis.Z{ + Member: value, + Score: float64(time.Now().Unix()), + }) + + return res, err +} + +// RemoveUser remove user from zset. +func (d *UserDispatchDao) RemoveUser(key, value string) (int64, error) { + res, err := (*d.cache).ZRem(d.ctx, key, value) + + return res, err +} + +// GetUser get user from zset top. +func (d *UserDispatchDao) GetUser(key string) (string, error) { + // Get latest and set timestamp to now + res, err := (*d.cache).ZRange(d.ctx, key, 0, 0) + if err != nil || len(res) < 1 { + return "", err + } + + // Update timestamp + value := res[0] + _, err = d.AddUser(key, value) + if err != nil { + return "", err + } + + return value, err +} diff --git a/server/internal/middleware/hooks/gen_jwt_hook.go b/server/internal/middleware/hooks/gen_jwt_hook.go index 42c91cd..c089fcf 100644 --- a/server/internal/middleware/hooks/gen_jwt_hook.go +++ b/server/internal/middleware/hooks/gen_jwt_hook.go @@ -44,6 +44,7 @@ func (h *JWT) Patterns() []string { "/api/v1/community/*", "/api/v1/admin/*", "/api/v1/platform/*", + "/api/v1/login/exit", // temp, will remove to OpenIM Callback } } diff --git a/server/internal/router/router.go b/server/internal/router/router.go index 5931bf7..89b704c 100644 --- a/server/internal/router/router.go +++ b/server/internal/router/router.go @@ -61,6 +61,7 @@ func InitRouter() *gin.Engine { login := apiv1.Group("/login") { login.POST("/account", api.AccountLogin) + login.POST("/exit", api.AccountExit) // user.POST("/email", api.GithubRegister) // user.POST("/github", api.GithubRegister) // user.POST("/wechat", api.GithubRegister) diff --git a/server/internal/service/user_dispatch.go b/server/internal/service/user_dispatch.go new file mode 100644 index 0000000..f35fce6 --- /dev/null +++ b/server/internal/service/user_dispatch.go @@ -0,0 +1,61 @@ +// Copyright © 2023 OpenIM open source community. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package service + +import ( + "context" + + "github.com/OpenIMSDK/OpenKF/server/internal/dal/dao" +) + +// USER_DISPATCH_QUEUE_KEY user dispatch queue key. +const USER_DISPATCH_QUEUE_KEY = "openkf:user_dispatch_queue" + +// UserDispatchService user service. +type UserDispatchService struct { + Service + + UserDispatchDao *dao.UserDispatchDao +} + +// NewUserDispatchService return new service with context. +func NewUserDispatchService(c context.Context) *UserDispatchService { + return &UserDispatchService{ + Service: Service{ + ctx: c, + }, + + UserDispatchDao: dao.NewUserDispatchDao(), + } +} + +// AddUser add user to enqueue. +func (s *UserDispatchService) AddUser(uuid string) error { + _, err := s.UserDispatchDao.AddUser(USER_DISPATCH_QUEUE_KEY, uuid) + + return err +} + +// GetUser get user and update timestamp. +func (s *UserDispatchService) GetUser() (string, error) { + return s.UserDispatchDao.GetUser(USER_DISPATCH_QUEUE_KEY) +} + +// DeleteUser delete user from queue. +func (s *UserDispatchService) DeleteUser(uuid string) error { + _, err := s.UserDispatchDao.RemoveUser(USER_DISPATCH_QUEUE_KEY, uuid) + + return err +} diff --git a/server/internal/service/user_dispatch_test.go b/server/internal/service/user_dispatch_test.go new file mode 100644 index 0000000..e9d25d8 --- /dev/null +++ b/server/internal/service/user_dispatch_test.go @@ -0,0 +1,44 @@ +// Copyright © 2023 OpenIM open source community. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package service + +import ( + "context" + "testing" + + "github.com/OpenIMSDK/OpenKF/server/internal/config" + "github.com/OpenIMSDK/OpenKF/server/internal/conn/db" + "github.com/OpenIMSDK/OpenKF/server/pkg/log" +) + +// TestUserQueue test user queue. +func TestUserQueue(t *testing.T) { + // Init + config.ConfigInit("../../config.yaml") + log.InitLogger() + db.InitMysqlDB() + db.InitRedisDB() + + s := NewUserDispatchService(context.Background()) + // err := s.AddUser("test3") + // if err != nil { + // t.Error(err) + // } + value, err := s.GetUser() + if err != nil { + t.Error(err) + } + t.Error(value) +} diff --git a/server/main.go b/server/main.go index a01b745..41ac86d 100644 --- a/server/main.go +++ b/server/main.go @@ -43,7 +43,6 @@ func init() { client.InitMinio() // client.InitMail() hooks.InitHooks() - slackcmd.InitSlackListen() } //go:generate go env -w GO111MODULE=on @@ -60,6 +59,9 @@ func init() { func main() { serverAddress := fmt.Sprintf("%s:%d", config.Config.Server.Ip, config.Config.Server.Port) + // Add slack server + go slackcmd.InitSlackListen() + r := router.InitRouter() s := server.InitServer(serverAddress, r) log.Error("server start error: %v", s.ListenAndServe().Error()) diff --git a/web/src/api/index/login.ts b/web/src/api/index/login.ts index faca2f9..d57dcfa 100644 --- a/web/src/api/index/login.ts +++ b/web/src/api/index/login.ts @@ -25,6 +25,7 @@ const API = { RegisterAdmin: '/register/admin', RegisterStaff: '/register/staff', AccountLogin: '/login/account', + AccountExit: '/login/exit', }; // Send email verification code @@ -60,3 +61,10 @@ export function accountLogin(data: AccountLoginParam) { data, }); } + +// User logout +export function accountLogout() { + return request.post({ + url: API.AccountExit, + }); +} \ No newline at end of file diff --git a/web/src/views/layouts/components/LayoutSideNav.vue b/web/src/views/layouts/components/LayoutSideNav.vue index c254a90..84c23a9 100644 --- a/web/src/views/layouts/components/LayoutSideNav.vue +++ b/web/src/views/layouts/components/LayoutSideNav.vue @@ -3,6 +3,7 @@ import { ref } from 'vue'; import { computed, onMounted } from 'vue'; import { useRouter } from 'vue-router'; import { useMenuStore } from '@/store'; +import { accountLogout } from '@/api/index/login'; const router = useRouter(); const menuStore = useMenuStore(); @@ -24,8 +25,9 @@ const changeHandler = (active:string) => { }; -const goHome = () => { +const goHome = async () => { router.push('/home/dashboard'); + await accountLogout() }; From b9d23b396139e4f61e93f5ec5ab70a1bd21f3151 Mon Sep 17 00:00:00 2001 From: IRONICBo <47499836+IRONICBo@users.noreply.github.com> Date: Fri, 11 Aug 2023 15:16:04 +0800 Subject: [PATCH 5/6] feat: (Merge) Interact with OpenIM Server 3.1. Signed-off-by: IRONICBo <47499836+IRONICBo@users.noreply.github.com> --- web/.eslintrc.js | 2 + web/package-lock.json | 8 +- web/package.json | 2 +- web/src/api/request/communityModel.ts | 1 + web/src/api/request/openimModel.ts | 4 +- web/src/store/index.ts | 2 + web/src/store/menu/index.ts | 9 +- web/src/store/openim_message/index.ts | 75 +++++++ web/src/store/openim_session/index.ts | 63 ++++++ web/src/store/user/index.ts | 27 ++- web/src/utils/common/im.ts | 37 ++++ web/src/utils/common/time.ts | 14 ++ web/src/utils/request/index.ts | 2 +- web/src/views/config/components/BotTable.vue | 2 +- .../views/config/components/ConfigForm.vue | 8 +- .../views/config/components/ConfigList.vue | 30 ++- .../views/config/components/GroupTable.vue | 2 +- .../config/components/KnowledgeBaseTable.vue | 2 +- web/src/views/config/index.less | 6 + .../views/dashboard/components/MemberCard.vue | 2 +- web/src/views/dashboard/index.vue | 10 +- .../layouts/components/LayoutSideNav.vue | 16 +- web/src/views/layouts/im.ts | 110 ++++++++++ web/src/views/layouts/index.vue | 50 +++++ web/src/views/login/components/LoginForm.vue | 7 +- .../views/login/components/RegisterForm.vue | 3 +- web/src/views/platform/index.vue | 4 +- .../views/session/components/SessionList.vue | 91 ++++++++ .../session/components/SessionWindow.vue | 200 ++++++++++++++++++ web/src/views/session/index.less | 160 ++++++++++++++ web/src/views/session/index.vue | 25 ++- 31 files changed, 930 insertions(+), 44 deletions(-) create mode 100644 web/src/store/openim_message/index.ts create mode 100644 web/src/store/openim_session/index.ts create mode 100644 web/src/utils/common/im.ts create mode 100644 web/src/views/layouts/im.ts create mode 100644 web/src/views/session/components/SessionList.vue create mode 100644 web/src/views/session/components/SessionWindow.vue create mode 100644 web/src/views/session/index.less diff --git a/web/.eslintrc.js b/web/.eslintrc.js index e2c18b9..5ec1698 100644 --- a/web/.eslintrc.js +++ b/web/.eslintrc.js @@ -47,5 +47,7 @@ module.exports = { '@typescript-eslint/ban-types': 'off', 'vue/multi-word-component-names': 'off', 'vue/comment-directive': 'off', + 'no-async-promise-executor': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', }, }; diff --git a/web/package-lock.json b/web/package-lock.json index a8b6ff5..317d109 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -12,7 +12,7 @@ "echarts": "^5.4.2", "less-loader": "^11.1.3", "lodash": "^4.17.21", - "open-im-sdk-wasm": "^0.1.1", + "open-im-sdk-wasm": "^3.1.0", "pinia": "^2.1.4", "pinia-plugin-persistedstate": "^3.1.0", "qs": "^6.11.2", @@ -2864,9 +2864,9 @@ } }, "node_modules/open-im-sdk-wasm": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/open-im-sdk-wasm/-/open-im-sdk-wasm-0.1.1.tgz", - "integrity": "sha512-I12XLvP2YwaPPWUCTaOCkqoRuqEx3A1aMvS2f9ouzCMtiFa6FRDLT7koVIri2+XtM8MmEB1XZAxwt3jYBb22gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/open-im-sdk-wasm/-/open-im-sdk-wasm-3.1.0.tgz", + "integrity": "sha512-Q1VpRCfV6vUrpZKzL/G351zsLeRxByV6FecKWB4blHzJEhXVH47DxbHjXduOs4MU0IcYYRqXiQseiVLsYHUKkQ==", "dependencies": { "@jlongster/sql.js": "^1.6.7", "absurd-sql": "^0.0.53", diff --git a/web/package.json b/web/package.json index 1b7d104..9037c09 100644 --- a/web/package.json +++ b/web/package.json @@ -14,7 +14,7 @@ "echarts": "^5.4.2", "less-loader": "^11.1.3", "lodash": "^4.17.21", - "open-im-sdk-wasm": "^0.1.1", + "open-im-sdk-wasm": "^3.1.0", "pinia": "^2.1.4", "pinia-plugin-persistedstate": "^3.1.0", "qs": "^6.11.2", diff --git a/web/src/api/request/communityModel.ts b/web/src/api/request/communityModel.ts index 2b3329d..5614768 100644 --- a/web/src/api/request/communityModel.ts +++ b/web/src/api/request/communityModel.ts @@ -2,6 +2,7 @@ export interface CommunityInfo { avatar: string; name: string; email: string; + description: string; } export type CreateCommunityParam = CommunityInfo; diff --git a/web/src/api/request/openimModel.ts b/web/src/api/request/openimModel.ts index bd143e2..87812bc 100644 --- a/web/src/api/request/openimModel.ts +++ b/web/src/api/request/openimModel.ts @@ -1,3 +1,3 @@ -import { LoginParam } from '@/utils/open-im-sdk-wasm/types/params'; +import { InitAndLoginConfig } from '@/utils/open-im-sdk-wasm/types/params'; -export type IMLoginParam = LoginParam; +export type IMLoginParam = InitAndLoginConfig; diff --git a/web/src/store/index.ts b/web/src/store/index.ts index 7f62c9a..7e14ee2 100644 --- a/web/src/store/index.ts +++ b/web/src/store/index.ts @@ -8,5 +8,7 @@ export { store }; export * from './menu'; export * from './user'; +export * from './openim_session'; +export * from './openim_message'; export default store; diff --git a/web/src/store/menu/index.ts b/web/src/store/menu/index.ts index 31b6002..9f6185a 100644 --- a/web/src/store/menu/index.ts +++ b/web/src/store/menu/index.ts @@ -2,6 +2,7 @@ import { defineStore } from 'pinia'; import usePermissionStore from '@/store'; import type { MenuRoute } from '@/types/interface' import type { MenuInfoResponse } from '@/api/response/menuModel'; +import store from "../index"; const MockMenuInfo: MenuInfoResponse[] = [ { @@ -36,7 +37,7 @@ const MockMenuInfo: MenuInfoResponse[] = [ } ] -export const useMenuStore = defineStore('menu', { +const useStore = defineStore('menu', { state: () => ({ menu_routes: MockMenuInfo, }), @@ -46,4 +47,8 @@ export const useMenuStore = defineStore('menu', { // TODO: fetch menu info } } -}); \ No newline at end of file +}); + +export default function useMenuStore() { + return useStore(store); +} diff --git a/web/src/store/openim_message/index.ts b/web/src/store/openim_message/index.ts new file mode 100644 index 0000000..a441bce --- /dev/null +++ b/web/src/store/openim_message/index.ts @@ -0,0 +1,75 @@ +import { OpenIM } from '@/api/openim'; +import { MessageItem } from "open-im-sdk-wasm/lib/types/entity"; +import { GetAdvancedHistoryMsgParams } from "open-im-sdk-wasm/lib/types/params"; +import { defineStore } from "pinia"; +import store from "../index"; + +interface StateType { + historyMessageList: MessageItem[]; + hasMore: boolean; +} + +interface IAdvancedMessageResponse { + lastMinSeq: number; + isEnd: boolean; + messageList: MessageItem[]; +} + +type GetHistoryMessageListFromReqResp = { + messageIDList: string[]; + lastMinSeq: number; +}; + +const useStore = defineStore("message", { + state: (): StateType => ({ + historyMessageList: [], + hasMore: true, + }), + getters: { + storeHistoryMessageList: (state) => state.historyMessageList, + storeHistoryMessageHasMore: (state) => state.hasMore, + }, + actions: { + async getHistoryMessageListFromReq( + params: GetAdvancedHistoryMsgParams + ): Promise { + const isFirstPage = + params.startClientMsgID === "" || params.lastMinSeq === 0; + try { + const { data } = await OpenIM.getAdvancedHistoryMessageList(params); + this.historyMessageList = [ + ...data.messageList, + ...(isFirstPage ? [] : this.historyMessageList), + ]; + this.hasMore = !data.isEnd && data.messageList.length === 20; + return { + messageIDList: data.messageList.map( + (message: MessageItem) => message.clientMsgID + ), + lastMinSeq: data.lastMinSeq, + }; + } catch (error) { + console.log("Get history message failed", error); + this.hasMore = false; + return { + messageIDList: [], + lastMinSeq: 0, + }; + } + }, + pushNewMessage(message: MessageItem) { + this.historyMessageList.push(message); + }, + clearHistoryMessage() { + this.historyMessageList = []; + }, + resetHistoryMessageList() { + this.historyMessageList = []; + this.hasMore = true; + }, + }, +}); + +export default function useMessageStore() { + return useStore(store); +} diff --git a/web/src/store/openim_session/index.ts b/web/src/store/openim_session/index.ts new file mode 100644 index 0000000..9b909b6 --- /dev/null +++ b/web/src/store/openim_session/index.ts @@ -0,0 +1,63 @@ +import { OpenIM } from '@/api/openim'; +import { + ConversationItem, +} from "@/utils/open-im-sdk-wasm/types/entity"; + +import { defineStore } from "pinia"; +import store from "../index"; + +interface StateType { + conversationList: ConversationItem[]; + currentConversation: ConversationItem; + unReadCount: number; +} + +const useStore = defineStore("session", { + state: (): StateType => ({ + conversationList: [], + currentConversation: {} as ConversationItem, + unReadCount: 0, + }), + getters: { + storeConversationList: (state) => state.conversationList, + storeCurrentConversation: (state) => state.currentConversation, + storeUnReadCount: (state) => state.unReadCount, + }, + actions: { + async getConversationListFromReq(): Promise { + try { + const { data } = await OpenIM.getAllConversationList(); + this.conversationList = data; + console.log('getConversationListFromReq', data); + return true; + } catch (error) { + return false; + } + }, + async getUnReadCountFromReq() { + const { data } = await OpenIM.getTotalUnreadMsgCount(); + this.unReadCount = data; + }, + updateUnReadCount(data: number) { + this.unReadCount = data; + }, + updateCurrentConversation(item: ConversationItem) { + this.currentConversation = { ...item }; + }, + updateConversationList(list: ConversationItem[]) { + this.conversationList = [...list]; + }, + delConversationByCID(conversationID: string) { + const idx = this.conversationList.findIndex( + (cve) => cve.conversationID === conversationID + ); + if (idx !== -1) { + this.conversationList.splice(idx, 1); + } + }, + }, +}); + +export default function useSessionStore() { + return useStore(store); +} diff --git a/web/src/store/user/index.ts b/web/src/store/user/index.ts index 53ab7c5..050e69b 100644 --- a/web/src/store/user/index.ts +++ b/web/src/store/user/index.ts @@ -7,6 +7,7 @@ import type { GetCommunityInfoResponse } from '@/api/response/communityModel'; import { defineStore } from 'pinia'; import { getMyCommunityInfo } from '@/api/index/community'; import { getMyInfo } from '@/api/index/user'; +import store from "../index"; const InitUserInfo: GetUserInfoResponse = { uuid: '', @@ -28,14 +29,23 @@ const InitCommunityInfo: GetCommunityInfoResponse = { }; const InitToken: TokenResponse = { - token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3V1aWQiOiI5NmUwNGQ5Mi1mZTkwLTRmMGEtYWU4MC1hZTg4YWRmNWY2ZGIiLCJjb21tdW5pdHlfdXVpZCI6ImNmZjkxMjEwLWE5MGEtNGNjYi04MmI3LWI1MzU2M2E3NzkxOSIsImlzcyI6Im9wZW5rZiIsImV4cCI6MTY5MTMxOTM3OCwibmJmIjoxNjkxMjMyOTc4fQ.PaCXrxjP_WyJSY8yishU5jCouRZ1cgpVrh5OSMVBnh0', + token: '', expire_time_seconds: 0, }; -export const useUserStore = defineStore('user', { +const InitKFToken: TokenResponse = { + token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3V1aWQiOiI1NTUyNDhhMWQxNDA5YTdhYmI1ODMwZmRhZDVkIiwiY29tbXVuaXR5X3V1aWQiOiI4NjNiOTExMjdmNGYwODhlZDQ2MjZjMzU3MTYyIiwiaXNzIjoib3BlbmtmIiwiZXhwIjoxNjkxNzI5MzgwLCJuYmYiOjE2OTE2NDI5ODB9.JomPDoVlrRYHWr03yIWqHbPaIQfuXdACEkhw4Ccwm0c', + expire_time_seconds: 0, +}; +const InitIMToken: TokenResponse = { + token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiI1NTUyNDhhMWQxNDA5YTdhYmI1ODMwZmRhZDVkIiwiUGxhdGZvcm1JRCI6NSwiZXhwIjoxNjk5NDE4OTgwLCJuYmYiOjE2OTE2NDI2ODAsImlhdCI6MTY5MTY0Mjk4MH0.btYIWiNc21o7sowLddv-6k9qEKQdiWlTFnQraX3eAYE', + expire_time_seconds: 0, +}; + +const useStore = defineStore('user', { state: () => ({ - kf_token: { ...InitToken }, - im_token: { ...InitToken }, + kf_token: { ...InitKFToken }, + im_token: { ...InitIMToken }, userInfo: { ...InitUserInfo }, communityInfo: { ...InitCommunityInfo }, }), @@ -50,7 +60,7 @@ export const useUserStore = defineStore('user', { const [userInfo, communityInfo] = await Promise.all([ getMyInfo() .then(res => { - console.log('getMyInfo success', res); + // console.log('getMyInfo success', res); return res; }) .catch(res => { @@ -59,7 +69,7 @@ export const useUserStore = defineStore('user', { }), // if error, set to default value getMyCommunityInfo() .then(res => { - console.log('getMyCommunityInfo success', res); + // console.log('getMyCommunityInfo success', res); return res; }) .catch(res => { @@ -78,3 +88,8 @@ export const useUserStore = defineStore('user', { }, }, }); + +export default function useUserStore() { + return useStore(store); + } + \ No newline at end of file diff --git a/web/src/utils/common/im.ts b/web/src/utils/common/im.ts new file mode 100644 index 0000000..f33ff83 --- /dev/null +++ b/web/src/utils/common/im.ts @@ -0,0 +1,37 @@ +import { + ConversationItem, +} from "@/utils/open-im-sdk-wasm/types/entity"; + +export const conversationSort = (conversationList: ConversationItem[]) => { + const arr: string[] = []; + const filterArr = conversationList.filter( + (c) => !arr.includes(c.conversationID) && arr.push(c.conversationID) + ); + filterArr.sort((a, b) => { + if (a.isPinned === b.isPinned) { + if (!(a.latestMsgSendTime && b.latestMsgSendTime)) { + return 0; + } + const aCompare = + a.draftTextTime! > a.latestMsgSendTime! + ? a.draftTextTime! + : a.latestMsgSendTime!; + const bCompare = + b.draftTextTime! > b.latestMsgSendTime! + ? b.draftTextTime! + : b.latestMsgSendTime!; + if (aCompare > bCompare) { + return -1; + } else if (aCompare < bCompare) { + return 1; + } else { + return 0; + } + } else if (a.isPinned && !b.isPinned) { + return -1; + } else { + return 1; + } + }); + return filterArr; +}; \ No newline at end of file diff --git a/web/src/utils/common/time.ts b/web/src/utils/common/time.ts index 438d5e8..f9c3794 100644 --- a/web/src/utils/common/time.ts +++ b/web/src/utils/common/time.ts @@ -43,4 +43,18 @@ export const getNowDiffDays = (date: string): number => { const diffTime = Math.abs(new Date(date).getTime() - new Date().getTime()); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); return diffDays; +} + +// Return a time from date +// if date is today, return time +// if date is yesterday, return yesterday +// if date is before yesterday, return date +export const getTimeFromDate = (timestamp: number | string): string => { + const dat = new Date(timestamp); + const diffDays = getNowDiffDays(dat.toString()); + if (diffDays === 0) { + return dat.getHours() + ':' + dat.getMinutes(); + } else { + return dat.getDate() + '/' + (dat.getMonth() + 1) + '/' + dat.getFullYear(); + } } \ No newline at end of file diff --git a/web/src/utils/request/index.ts b/web/src/utils/request/index.ts index 33c6aee..0852f5a 100644 --- a/web/src/utils/request/index.ts +++ b/web/src/utils/request/index.ts @@ -7,7 +7,7 @@ import { ContentTypeEnum } from '@/constants'; import { VAxios } from './axios'; import type { AxiosTransform, CreateAxiosOptions } from './transform'; import { formatRequestDate, joinTimestamp, setObjToUrlParams } from './utils'; -import { useUserStore } from '@/store'; +import useUserStore from '@/store/user'; // Default API url const host = import.meta.env.VITE_API_URL; diff --git a/web/src/views/config/components/BotTable.vue b/web/src/views/config/components/BotTable.vue index 59198e8..3dd84e1 100644 --- a/web/src/views/config/components/BotTable.vue +++ b/web/src/views/config/components/BotTable.vue @@ -197,7 +197,7 @@ const COLUMNS: PrimaryTableCol[] = [ >