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 } // 员工表按 (tid, account) 唯一:同一账号在不同企业各有一份员工档案, // 因此这里必须带上 tid,否则会把其他企业的同账号员工误判为本企业已有。 var emp models.BackendEmployee err := models.Orm.QueryTable(new(models.BackendEmployee)). Filter("tid", tid). Filter("account", account). One(&emp) 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 }