更新
This commit is contained in:
1 parent
b6b528490c
commit
4ab63e77ed
28 files changed
+1858
-695
No files matched your search
@@ -0,0 +1,379 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"server/models"
|
||||
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
)
|
||||
|
||||
// OnEmployeeCreatedHook 员工创建后的外部回调(由 controllers 包在初始化时注册,如同步通讯录)
|
||||
var OnEmployeeCreatedHook func(tid uint64, employeeID uint64, emp *models.BackendEmployee)
|
||||
|
||||
// parseDate 解析日期字符串为 *time.Time
|
||||
func parseDate(v string) *time.Time {
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
if t, err := time.Parse("2006-01-02", v); err == nil {
|
||||
return &t
|
||||
}
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", v); err == nil {
|
||||
return &t
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnsureEmployeeForTenantUser 确保租户用户在 yz_backend_employee 中有对应的人员记录。
|
||||
// 规则:先有用户,后自动生成员工,以 (tid, account) 严格匹配。
|
||||
func EnsureEmployeeForTenantUser(tu *models.SystemTenantUser) (*models.BackendEmployee, bool, error) {
|
||||
if tu == nil || tu.Account == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
account := strings.TrimSpace(*tu.Account)
|
||||
if account == "" {
|
||||
return nil, false, nil
|
||||
}
|
||||
tid := int(tu.Tid)
|
||||
if tid <= 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
var emp models.BackendEmployee
|
||||
// 员工表的 uk_account 是全局唯一索引(不是 tid+account 复合索引)。
|
||||
// 因此先按账号查全表,避免已有的历史/其他租户同账号记录被误判为缺失后再 INSERT。
|
||||
err := models.Orm.QueryTable(new(models.BackendEmployee)).
|
||||
Filter("account", account).
|
||||
One(&emp)
|
||||
if err == nil && emp.Tid != nil && *emp.Tid > 0 && *emp.Tid != tid {
|
||||
return nil, false, fmt.Errorf("员工账号 %s 已被其他租户使用,不能重复创建", account)
|
||||
}
|
||||
|
||||
name := account
|
||||
if tu.Name != nil && strings.TrimSpace(*tu.Name) != "" {
|
||||
name = strings.TrimSpace(*tu.Name)
|
||||
}
|
||||
|
||||
var birthday *time.Time
|
||||
if tu.Birth != nil && strings.TrimSpace(*tu.Birth) != "" {
|
||||
birthday = parseDate(strings.TrimSpace(*tu.Birth))
|
||||
}
|
||||
|
||||
status := int8(1)
|
||||
if tu.Status == 0 {
|
||||
status = 0
|
||||
}
|
||||
|
||||
var deptPtr *string
|
||||
if tu.OrgID > 0 {
|
||||
deptStr := strconv.FormatUint(tu.OrgID, 10)
|
||||
deptPtr = &deptStr
|
||||
}
|
||||
|
||||
if err == orm.ErrNoRows {
|
||||
// 不存在 -> 自动创建新员工
|
||||
newEmp := models.BackendEmployee{
|
||||
Tid: &tid,
|
||||
Account: account,
|
||||
Name: name,
|
||||
Gender: int8(tu.Sex),
|
||||
Birthday: birthday,
|
||||
Department: deptPtr,
|
||||
Phone: tu.Phone,
|
||||
Email: tu.Email,
|
||||
AccountStatus: status,
|
||||
}
|
||||
id, insertErr := models.Orm.Insert(&newEmp)
|
||||
if insertErr != nil {
|
||||
return nil, false, insertErr
|
||||
}
|
||||
newEmp.ID = uint(id)
|
||||
|
||||
if OnEmployeeCreatedHook != nil {
|
||||
OnEmployeeCreatedHook(tu.Tid, uint64(newEmp.ID), &newEmp)
|
||||
}
|
||||
|
||||
return &newEmp, true, nil
|
||||
} else if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// 已存在 -> 只同步用户侧明确提供的基础资料;不覆盖部门、职位及其他员工专属字段。
|
||||
update := orm.Params{}
|
||||
if emp.Tid == nil || *emp.Tid <= 0 {
|
||||
// 历史无租户归属的员工记录:被当前租户用户明确匹配后,补上租户归属。
|
||||
update["tid"] = tid
|
||||
}
|
||||
if emp.DeleteTime != nil {
|
||||
update["delete_time"] = nil
|
||||
}
|
||||
if emp.Name != name {
|
||||
update["name"] = name
|
||||
}
|
||||
if emp.Gender != int8(tu.Sex) {
|
||||
update["gender"] = int8(tu.Sex)
|
||||
}
|
||||
if birthday != nil && (emp.Birthday == nil || !emp.Birthday.Equal(*birthday)) {
|
||||
update["birthday"] = birthday
|
||||
}
|
||||
if tu.Phone != nil && strings.TrimSpace(*tu.Phone) != "" && (emp.Phone == nil || *emp.Phone != *tu.Phone) {
|
||||
update["phone"] = *tu.Phone
|
||||
}
|
||||
if tu.Email != nil && strings.TrimSpace(*tu.Email) != "" && (emp.Email == nil || *emp.Email != *tu.Email) {
|
||||
update["email"] = *tu.Email
|
||||
}
|
||||
if tu.OrgID > 0 && (emp.Department == nil || *emp.Department == "") {
|
||||
update["department"] = *deptPtr
|
||||
}
|
||||
if emp.AccountStatus != status && emp.AccountStatus != 2 {
|
||||
update["account_status"] = status
|
||||
}
|
||||
if len(update) > 0 {
|
||||
update["update_time"] = time.Now()
|
||||
query := models.Orm.QueryTable(new(models.BackendEmployee)).Filter("id", emp.ID)
|
||||
if emp.Tid != nil && *emp.Tid > 0 {
|
||||
query = query.Filter("tid", tid)
|
||||
}
|
||||
if _, err := query.Update(update); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
return &emp, false, nil
|
||||
}
|
||||
|
||||
// SyncSelectedTenantUsersToEmployees 对指定租户用户进行比对;仅对缺失或基础资料有差异的员工执行写入。
|
||||
func SyncSelectedTenantUsersToEmployees(tid uint64, userIDs []uint64) (map[string]interface{}, error) {
|
||||
counts := map[string]interface{}{"created": 0, "updated": 0, "unchanged": 0, "failed": 0, "errors": []string{}}
|
||||
if tid == 0 || len(userIDs) == 0 {
|
||||
return counts, nil
|
||||
}
|
||||
seen := make(map[uint64]struct{}, len(userIDs))
|
||||
for _, userID := range userIDs {
|
||||
if userID == 0 {
|
||||
counts["failed"] = counts["failed"].(int) + 1
|
||||
counts["errors"] = append(counts["errors"].([]string), "用户ID无效")
|
||||
continue
|
||||
}
|
||||
if _, duplicate := seen[userID]; duplicate {
|
||||
continue
|
||||
}
|
||||
seen[userID] = struct{}{}
|
||||
|
||||
var user models.SystemTenantUser
|
||||
err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("id", userID).
|
||||
Filter("tid", tid).
|
||||
Filter("delete_time__isnull", true).
|
||||
One(&user)
|
||||
if err != nil {
|
||||
counts["failed"] = counts["failed"].(int) + 1
|
||||
counts["errors"] = append(counts["errors"].([]string), fmt.Sprintf("用户记录 %d 不存在于当前租户或已删除", userID))
|
||||
continue
|
||||
}
|
||||
|
||||
differs := tenantUserEmployeeDiffers(&user)
|
||||
_, created, err := EnsureEmployeeForTenantUser(&user)
|
||||
if err != nil {
|
||||
counts["failed"] = counts["failed"].(int) + 1
|
||||
account := ""
|
||||
if user.Account != nil {
|
||||
account = strings.TrimSpace(*user.Account)
|
||||
}
|
||||
counts["errors"] = append(counts["errors"].([]string), fmt.Sprintf("用户 %s(ID %d)同步失败: %v", account, userID, err))
|
||||
continue
|
||||
}
|
||||
if created {
|
||||
counts["created"] = counts["created"].(int) + 1
|
||||
} else if differs {
|
||||
counts["updated"] = counts["updated"].(int) + 1
|
||||
} else {
|
||||
counts["unchanged"] = counts["unchanged"].(int) + 1
|
||||
}
|
||||
}
|
||||
return counts, nil
|
||||
}
|
||||
|
||||
func tenantUserEmployeeDiffers(user *models.SystemTenantUser) bool {
|
||||
if user == nil || user.Account == nil {
|
||||
return true
|
||||
}
|
||||
var employee models.BackendEmployee
|
||||
if err := models.Orm.QueryTable(new(models.BackendEmployee)).
|
||||
Filter("tid", int(user.Tid)).
|
||||
Filter("account", strings.TrimSpace(*user.Account)).
|
||||
Filter("delete_time__isnull", true).
|
||||
One(&employee); err != nil {
|
||||
return true
|
||||
}
|
||||
name := strings.TrimSpace(*user.Account)
|
||||
if user.Name != nil && strings.TrimSpace(*user.Name) != "" {
|
||||
name = strings.TrimSpace(*user.Name)
|
||||
}
|
||||
status := int8(1)
|
||||
if user.Status == 0 {
|
||||
status = 0
|
||||
}
|
||||
if employee.Name != name || employeeStatusDiffers(employee.AccountStatus, status) || int(employee.Gender) != int(user.Sex) {
|
||||
return true
|
||||
}
|
||||
if user.Phone != nil && (employee.Phone == nil || *employee.Phone != *user.Phone) {
|
||||
return true
|
||||
}
|
||||
if user.Email != nil && (employee.Email == nil || *employee.Email != *user.Email) {
|
||||
return true
|
||||
}
|
||||
if user.Birth != nil && strings.TrimSpace(*user.Birth) != "" {
|
||||
birthday := parseDate(*user.Birth)
|
||||
if birthday != nil && (employee.Birthday == nil || !employee.Birthday.Equal(*birthday)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func employeeStatusDiffers(employeeStatus, userStatus int8) bool {
|
||||
if employeeStatus == 2 {
|
||||
return false // 离职员工由人事管理维护,不通过用户同步自动复职
|
||||
}
|
||||
return employeeStatus != userStatus
|
||||
}
|
||||
|
||||
// SyncEmployeeOnUserUpdate 当用户在用户管理中被编辑时,同步修改对应员工
|
||||
func SyncEmployeeOnUserUpdate(tid uint64, oldAccount string, tu *models.SystemTenantUser) error {
|
||||
if tu == nil || tid == 0 {
|
||||
return nil
|
||||
}
|
||||
acc := oldAccount
|
||||
if acc == "" && tu.Account != nil {
|
||||
acc = *tu.Account
|
||||
}
|
||||
acc = strings.TrimSpace(acc)
|
||||
if acc == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var emp models.BackendEmployee
|
||||
err := models.Orm.QueryTable(new(models.BackendEmployee)).
|
||||
Filter("tid", int(tid)).
|
||||
Filter("account", acc).
|
||||
One(&emp)
|
||||
if err != nil {
|
||||
// 员工不存在则自动创建
|
||||
_, _, err = EnsureEmployeeForTenantUser(tu)
|
||||
return err
|
||||
}
|
||||
|
||||
changed := false
|
||||
if tu.Account != nil && strings.TrimSpace(*tu.Account) != "" && emp.Account != strings.TrimSpace(*tu.Account) {
|
||||
emp.Account = strings.TrimSpace(*tu.Account)
|
||||
changed = true
|
||||
}
|
||||
if tu.Name != nil && strings.TrimSpace(*tu.Name) != "" && emp.Name != strings.TrimSpace(*tu.Name) {
|
||||
emp.Name = strings.TrimSpace(*tu.Name)
|
||||
changed = true
|
||||
}
|
||||
if tu.Sex > 0 && emp.Gender != int8(tu.Sex) {
|
||||
emp.Gender = int8(tu.Sex)
|
||||
changed = true
|
||||
}
|
||||
if tu.Birth != nil {
|
||||
tb := parseDate(strings.TrimSpace(*tu.Birth))
|
||||
if tb != nil && (emp.Birthday == nil || !emp.Birthday.Equal(*tb)) {
|
||||
emp.Birthday = tb
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if tu.Phone != nil && (emp.Phone == nil || *emp.Phone != *tu.Phone) {
|
||||
emp.Phone = tu.Phone
|
||||
changed = true
|
||||
}
|
||||
if tu.Email != nil && (emp.Email == nil || *emp.Email != *tu.Email) {
|
||||
emp.Email = tu.Email
|
||||
changed = true
|
||||
}
|
||||
if tu.Status == 1 && emp.AccountStatus == 0 {
|
||||
emp.AccountStatus = 1
|
||||
changed = true
|
||||
} else if tu.Status == 0 && emp.AccountStatus == 1 {
|
||||
emp.AccountStatus = 0
|
||||
changed = true
|
||||
}
|
||||
|
||||
if changed {
|
||||
emp.UpdateTime = time.Now()
|
||||
_, err = models.Orm.Update(&emp)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SyncEmployeeOnUserDelete 当租户用户被删除时,将员工置为离职状态(2)
|
||||
func SyncEmployeeOnUserDelete(tid uint64, account string) error {
|
||||
account = strings.TrimSpace(account)
|
||||
if tid == 0 || account == "" {
|
||||
return nil
|
||||
}
|
||||
_, err := models.Orm.QueryTable(new(models.BackendEmployee)).
|
||||
Filter("tid", int(tid)).
|
||||
Filter("account", account).
|
||||
Filter("delete_time__isnull", true).
|
||||
Update(orm.Params{
|
||||
"account_status": int8(2), // 2 = 离职
|
||||
"update_time": time.Now(),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// SyncUserFromEmployee 当在人员管理中编辑了员工的基本信息时,同步回用户表
|
||||
func SyncUserFromEmployee(tid uint64, emp *models.BackendEmployee) error {
|
||||
if emp == nil || tid == 0 {
|
||||
return nil
|
||||
}
|
||||
account := strings.TrimSpace(emp.Account)
|
||||
if account == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
update := orm.Params{
|
||||
"update_time": time.Now(),
|
||||
}
|
||||
if emp.Name != "" {
|
||||
update["name"] = emp.Name
|
||||
}
|
||||
if emp.Phone != nil {
|
||||
update["phone"] = *emp.Phone
|
||||
}
|
||||
if emp.Email != nil {
|
||||
update["email"] = *emp.Email
|
||||
}
|
||||
if emp.Gender > 0 {
|
||||
update["sex"] = uint8(emp.Gender)
|
||||
}
|
||||
if emp.Birthday != nil {
|
||||
update["birth"] = emp.Birthday.Format("2006-01-02")
|
||||
}
|
||||
if emp.Department != nil && *emp.Department != "" {
|
||||
if orgId, err := strconv.ParseUint(*emp.Department, 10, 64); err == nil && orgId > 0 {
|
||||
update["org_id"] = orgId
|
||||
}
|
||||
}
|
||||
// 员工状态 1: 启用 -> 用户 1; 员工状态 0: 禁用 / 2: 离职 -> 用户 0 (禁用登录)
|
||||
if emp.AccountStatus == 1 {
|
||||
update["status"] = int8(1)
|
||||
} else {
|
||||
update["status"] = int8(0)
|
||||
}
|
||||
|
||||
_, err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("tid", tid).
|
||||
Filter("account", account).
|
||||
Filter("delete_time__isnull", true).
|
||||
Update(update)
|
||||
return err
|
||||
}
|
||||
Reference in new issue
Block a user