mirror of
https://github.com/ccfos/nightingale.git
synced 2026-03-02 22:19:10 +00:00
267 lines
6.4 KiB
Go
267 lines
6.4 KiB
Go
package flashduty
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/ccfos/nightingale/v6/models"
|
|
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
|
|
|
"github.com/toolkits/pkg/logger"
|
|
)
|
|
|
|
func SyncUsersChange(ctx *ctx.Context, dbUsers []*models.User) error {
|
|
if !ctx.IsCenter {
|
|
return nil
|
|
}
|
|
|
|
appKey, err := models.ConfigsGetFlashDutyAppKey(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req := make(map[string]interface{})
|
|
req["limit"] = 100
|
|
userList, err := PostFlashDutyWithResp[Data]("/member/list", appKey, req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
total := userList.Total
|
|
items := []Item{}
|
|
for i := 0; i < total/100+1; i++ {
|
|
req["p"] = i
|
|
req["limit"] = 100
|
|
resp, err := PostFlashDutyWithResp[Data]("/member/list", appKey, req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
items = append(items, resp.Items...)
|
|
}
|
|
|
|
dutyUsers := make(map[int64]*models.User, len(items))
|
|
for i := range items {
|
|
if items[i].RefID != "" {
|
|
id, _ := strconv.ParseInt(items[i].RefID, 10, 64)
|
|
user := &models.User{
|
|
Username: items[i].MemberName,
|
|
Email: items[i].Email,
|
|
Phone: items[i].Phone,
|
|
Id: id,
|
|
}
|
|
dutyUsers[id] = user
|
|
}
|
|
|
|
}
|
|
|
|
dbUsersHas := sliceToMap(dbUsers)
|
|
|
|
delUsers := diffMap(dutyUsers, dbUsersHas)
|
|
fdDelUsers(appKey, delUsers)
|
|
|
|
addUsers := diffMap(dbUsersHas, dutyUsers)
|
|
if err := fdAddUsers(appKey, addUsers); err != nil {
|
|
return err
|
|
}
|
|
updateUser(appKey, dbUsersHas, dutyUsers)
|
|
return nil
|
|
}
|
|
|
|
func sliceToMap(dbUsers []*models.User) map[int64]*models.User {
|
|
m := make(map[int64]*models.User, len(dbUsers))
|
|
for _, user := range dbUsers {
|
|
m[user.Id] = user
|
|
}
|
|
return m
|
|
}
|
|
|
|
// in m1 and not in m2
|
|
func diffMap(m1, m2 map[int64]*models.User) []models.User {
|
|
var diff []models.User
|
|
for i := range m1 {
|
|
if _, ok := m2[i]; !ok {
|
|
diff = append(diff, *m1[i])
|
|
}
|
|
}
|
|
return diff
|
|
}
|
|
func updateUser(appKey string, m1, m2 map[int64]*models.User) {
|
|
for i := range m1 {
|
|
if _, ok := m2[i]; ok {
|
|
if m1[i].Email != m2[i].Email || !PhoneIsSame(m1[i].Phone, m2[i].Phone) || m1[i].Username != m2[i].Username {
|
|
var flashdutyUser User
|
|
|
|
flashdutyUser = User{
|
|
RefID: strconv.FormatInt(m1[i].Id, 10),
|
|
}
|
|
flashdutyUser.Updates = Updates{
|
|
Phone: m1[i].Phone,
|
|
Email: m1[i].Email,
|
|
MemberName: m1[i].Username,
|
|
RefID: strconv.FormatInt(m1[i].Id, 10),
|
|
}
|
|
err := flashdutyUser.UpdateMember(appKey)
|
|
if err != nil {
|
|
logger.Errorf("failed to update user: %v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func PhoneIsSame(phone1, phone2 string) bool {
|
|
// 兼容不同国家/地区前缀,例如 +86、+1、+44 等,以及包含空格或短横线的格式
|
|
normalize := func(p string) string {
|
|
p = strings.TrimSpace(p)
|
|
p = strings.ReplaceAll(p, " ", "")
|
|
p = strings.ReplaceAll(p, "-", "")
|
|
p = strings.TrimPrefix(p, "+")
|
|
return p
|
|
}
|
|
|
|
p1 := normalize(phone1)
|
|
p2 := normalize(phone2)
|
|
|
|
if p1 == p2 {
|
|
return true
|
|
}
|
|
|
|
// 如果长度相差不超过 3 且较长的以较短的结尾,则认为是相同号码(忽略最多 3 位国家区号差异)
|
|
if len(p1) > len(p2) {
|
|
return len(p1)-len(p2) <= 3 && strings.HasSuffix(p1, p2)
|
|
}
|
|
return len(p2)-len(p1) <= 3 && strings.HasSuffix(p2, p1)
|
|
}
|
|
|
|
type User struct {
|
|
Email string `json:"email,omitempty"`
|
|
Phone string `json:"phone,omitempty"`
|
|
MemberName string `json:"member_name,omitempty"`
|
|
RefID string `json:"ref_id,omitempty"`
|
|
Updates Updates `json:"updates,omitempty"`
|
|
}
|
|
|
|
type Updates struct {
|
|
RefID string `json:"ref_id,omitempty"`
|
|
Email string `json:"email,omitempty"`
|
|
Phone string `json:"phone,omitempty"`
|
|
MemberName string `json:"member_name,omitempty"`
|
|
CountryCode string `json:"country_code,omitempty"`
|
|
}
|
|
|
|
func (user *User) delMember(appKey string) error {
|
|
if user.RefID == "" {
|
|
return errors.New("refID must not be empty")
|
|
}
|
|
userDel := &User{RefID: user.RefID}
|
|
return PostFlashDuty("/member/delete", appKey, userDel)
|
|
}
|
|
|
|
func (user *User) UpdateMember(appKey string) error {
|
|
|
|
return PostFlashDuty("/member/info/reset", appKey, user)
|
|
}
|
|
|
|
type Members struct {
|
|
Users []User `json:"members"`
|
|
}
|
|
|
|
func (m *Members) addMembers(appKey string) error {
|
|
if len(m.Users) == 0 {
|
|
return nil
|
|
}
|
|
validUsers := make([]User, 0, len(m.Users))
|
|
for _, user := range m.Users {
|
|
if user.RefID == "" || (user.Phone == "" && user.Email == "") {
|
|
logger.Errorf("user(%+v) refID must not be none, Email or Phone can not be none", user)
|
|
} else {
|
|
validUsers = append(validUsers, user)
|
|
}
|
|
}
|
|
if len(validUsers) == 0 {
|
|
return nil
|
|
}
|
|
m.Users = validUsers
|
|
return PostFlashDuty("/member/invite", appKey, m)
|
|
}
|
|
|
|
func fdAddUsers(appKey string, users []models.User) error {
|
|
fdUsers := usersToFdUsers(users)
|
|
members := &Members{
|
|
Users: fdUsers,
|
|
}
|
|
return members.addMembers(appKey)
|
|
}
|
|
|
|
func fdDelUsers(appKey string, users []models.User) {
|
|
fdUsers := usersToFdUsers(users)
|
|
for _, fdUser := range fdUsers {
|
|
if err := fdUser.delMember(appKey); err != nil {
|
|
logger.Errorf("failed to delete user: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func usersToFdUsers(users []models.User) []User {
|
|
fdUsers := make([]User, 0, len(users))
|
|
for i := range users {
|
|
fdUsers = append(fdUsers, User{
|
|
RefID: strconv.FormatInt(users[i].Id, 10),
|
|
Phone: users[i].Phone,
|
|
Email: users[i].Email,
|
|
MemberName: users[i].Username,
|
|
})
|
|
|
|
}
|
|
return fdUsers
|
|
}
|
|
|
|
func UpdateUser(ctx *ctx.Context, target models.User, email, phone string) {
|
|
//contact := target.FindSameContact(email, phone)
|
|
if target.Id == 0 {
|
|
logger.Errorf("user not found: %s", target.Username)
|
|
return
|
|
}
|
|
if email == "" && phone == "" {
|
|
logger.Errorf("email and phone are both empty: %s", target.Username)
|
|
return
|
|
}
|
|
var flashdutyUser User
|
|
refID := strconv.FormatInt(target.Id, 10)
|
|
|
|
flashdutyUser = User{
|
|
RefID: refID,
|
|
}
|
|
flashdutyUser.Updates = Updates{
|
|
Phone: phone,
|
|
Email: email,
|
|
MemberName: target.Username,
|
|
RefID: refID,
|
|
}
|
|
appKey, err := models.ConfigsGetFlashDutyAppKey(ctx)
|
|
if err != nil {
|
|
logger.Errorf("failed to get flashduty app key: %v", err)
|
|
return
|
|
}
|
|
err = flashdutyUser.UpdateMember(appKey)
|
|
if err != nil && strings.Contains(err.Error(), "no member found") {
|
|
// 如果没有找到成员,说明需要新建成员
|
|
NewUser := &User{
|
|
Phone: phone,
|
|
Email: email,
|
|
MemberName: target.Username,
|
|
RefID: refID,
|
|
}
|
|
err = PostFlashDuty("/member/invite", appKey, NewUser)
|
|
if err != nil {
|
|
logger.Errorf("failed to update user: %v", err)
|
|
}
|
|
return
|
|
|
|
}
|
|
if err != nil {
|
|
logger.Errorf("failed to update user: %v", err)
|
|
}
|
|
}
|