优化backend端相关功能

This commit is contained in:
2026-09-05 17:28:40 +08:00
parent 3d53f03246
commit 0f74abc5d4
25 changed files with 781 additions and 440 deletions
+110 -12
View File
@@ -66,6 +66,7 @@ type erpContactDTO struct {
WorkPhone string `json:"work_phone"`
Email string `json:"email"`
Wechat string `json:"wechat"`
QQ string `json:"qq"`
Avatar string `json:"avatar"`
CompanyName string `json:"company_name"`
DeptName string `json:"dept_name"`
@@ -116,6 +117,7 @@ func (c *BackendErpContactController) List() {
cond = cond.Or("phone__contains", keyword)
cond = cond.Or("email__contains", keyword)
cond = cond.Or("wechat__contains", keyword)
cond = cond.Or("qq__contains", keyword)
qs = qs.SetCond(cond)
}
@@ -126,7 +128,7 @@ func (c *BackendErpContactController) List() {
}
var rows []models.BackendErpContact
_, err = qs.OrderBy("-is_starred", "sort", "-id").
_, err = qs.OrderBy("-is_starred", "contact_name", "-id").
Limit(pageSize, (page-1)*pageSize).
All(&rows)
if err != nil {
@@ -189,6 +191,7 @@ func (c *BackendErpContactController) Create() {
workPhone, _ := c.contactGetStringValue(body, "work_phone")
email, _ := c.contactGetStringValue(body, "email")
wechat, _ := c.contactGetStringValue(body, "wechat")
qq, _ := c.contactGetStringValue(body, "qq")
avatar, _ := c.contactGetStringValue(body, "avatar")
companyName, _ := c.contactGetStringValue(body, "company_name")
deptName, _ := c.contactGetStringValue(body, "dept_name")
@@ -211,6 +214,7 @@ func (c *BackendErpContactController) Create() {
WorkPhone: contactStrPtr(workPhone),
Email: contactStrPtr(email),
Wechat: contactStrPtr(wechat),
QQ: contactStrPtr(qq),
Avatar: contactStrPtr(avatar),
CompanyName: contactStrPtr(companyName),
DeptName: contactStrPtr(deptName),
@@ -281,6 +285,9 @@ func (c *BackendErpContactController) Update() {
if v, has := c.contactGetStringValue(body, "wechat"); has {
update["wechat"] = contactNullableString(v)
}
if v, has := c.contactGetStringValue(body, "qq"); has {
update["qq"] = contactNullableString(v)
}
if v, has := c.contactGetStringValue(body, "avatar"); has {
update["avatar"] = contactNullableString(v)
}
@@ -555,6 +562,7 @@ func (c *BackendErpContactController) contactDTO(row models.BackendErpContact) e
WorkPhone: contactDerefString(row.WorkPhone),
Email: contactDerefString(row.Email),
Wechat: contactDerefString(row.Wechat),
QQ: contactDerefString(row.QQ),
Avatar: contactDerefString(row.Avatar),
CompanyName: contactDerefString(row.CompanyName),
DeptName: contactDerefString(row.DeptName),
@@ -571,11 +579,31 @@ func (c *BackendErpContactController) contactDTO(row models.BackendErpContact) e
// --- Sync from Employee ---
// SyncContactOnEmployeeCreate 员工创建后同步到通讯录(由 ERP 控制器调用)
// SyncContactOnEmployeeCreate 员工创建后同步到通讯录(由组织架构控制器调用)
func SyncContactOnEmployeeCreate(tid uint64, employeeID uint64, emp *models.BackendEmployee) {
syncContactFromEmployee(tid, employeeID, emp)
}
// SyncContactOnEmployeeRefresh 员工信息变更后重新全量同步通讯录(由组织架构控制器调用)。
// 走的是与建档同步同一套逻辑:先取员工基础字段,再用人事档案字段覆盖(工作邮箱、家庭地址优先)。
func SyncContactOnEmployeeRefresh(tid uint64, employeeID uint64) {
var emp models.BackendEmployee
if err := models.Orm.QueryTable(new(models.BackendEmployee)).
Filter("id", employeeID).
Filter("delete_time__isnull", true).
One(&emp); err != nil {
return
}
syncContactFromEmployee(tid, employeeID, &emp)
}
// SyncContactOnEmployeeFileChange 人事档案建档/变更后同步通讯录(由 OA 档案控制器调用)。
// 通讯录以 employee_id 关联员工,档案里的字段(工作邮箱、家庭地址)优先级高于员工自身字段。
// 只做字段同步:不会新增/删除员工,也不会清理通讯录记录。
func SyncContactOnEmployeeFileChange(tid uint64, employeeID uint64) {
SyncContactOnEmployeeRefresh(tid, employeeID)
}
// SyncContactOnEmployeeUpdate 员工更新后同步到通讯录(由 ERP 控制器调用)
func SyncContactOnEmployeeUpdate(employeeID uint64, update orm.Params) {
var contact models.BackendErpContact
@@ -638,11 +666,8 @@ func SyncContactOnEmployeeUpdate(employeeID uint64, update orm.Params) {
if v, ok := update["home_address"]; ok {
contactUpdate["address"] = v
}
if v, ok := update["account_status"]; ok {
if status, ok := v.(int8); ok && status == 2 {
contactUpdate["status"] = int8(0)
}
}
// 员工账号状态(启用/禁用/离职)不同步到通讯录:
// 通讯录是企业数据,员工离职或账号被删除都不代表联系人应当停用,是否停用由通讯录模块自行维护。
if len(contactUpdate) > 0 {
models.Orm.QueryTable(new(models.BackendErpContact)).
@@ -651,7 +676,9 @@ func SyncContactOnEmployeeUpdate(employeeID uint64, update orm.Params) {
}
}
// SyncContactOnEmployeeDelete 员工删除后同步通讯录(由 ERP 控制器调用)
// SyncContactOnEmployeeDelete 清理员工关联的通讯录记录。
// 注意:员工离职/禁用(deleteEmployee)与后台账号删除(deleteUser)都不得调用本函数——
// 通讯录是企业数据,账号或状态变化不等于联系人消失。仅在员工数据被真正物理清理时使用。
func SyncContactOnEmployeeDelete(employeeID uint64) {
now := time.Now().Format("2006-01-02 15:04:05")
models.Orm.QueryTable(new(models.BackendErpContact)).
@@ -661,6 +688,7 @@ func SyncContactOnEmployeeDelete(employeeID uint64) {
}
// syncContactFromEmployee 内部同步函数
// 从员工信息和人事档案同步数据到通讯录
func syncContactFromEmployee(tid uint64, employeeID uint64, emp *models.BackendEmployee) string {
var existing models.BackendErpContact
err := models.Orm.QueryTable(new(models.BackendErpContact)).
@@ -668,6 +696,13 @@ func syncContactFromEmployee(tid uint64, employeeID uint64, emp *models.BackendE
Filter("delete_time__isnull", true).
One(&existing)
// 获取人事档案数据
fileData := getEmployeeFileForContact(employeeID)
// 通讯录字段优先级:人事档案 > 员工(档案字段为空时回退员工自身字段)
email := contactFirstNonEmptyPtr(&fileData.WorkEmail, emp.Email)
address := contactFirstNonEmptyPtr(&fileData.HouseholdAddress, &fileData.CurrentAddress, emp.HomeAddress)
if err != nil {
// Not found - create new contact
var orgID *uint64
@@ -685,10 +720,10 @@ func syncContactFromEmployee(tid uint64, employeeID uint64, emp *models.BackendE
ContactName: emp.Name,
Gender: emp.Gender,
Phone: emp.Phone,
Email: emp.Email,
Email: email,
Wechat: emp.Wechat,
PositionTitle: emp.Position,
Address: emp.HomeAddress,
Address: address,
Status: 1,
}
models.Orm.Insert(&contact)
@@ -700,10 +735,10 @@ func syncContactFromEmployee(tid uint64, employeeID uint64, emp *models.BackendE
"contact_name": emp.Name,
"gender": emp.Gender,
"phone": emp.Phone,
"email": emp.Email,
"email": email,
"wechat": emp.Wechat,
"position_title": emp.Position,
"address": emp.HomeAddress,
"address": address,
}
if emp.Department != nil {
if oid, err := strconv.ParseUint(strings.TrimSpace(*emp.Department), 10, 64); err == nil && oid > 0 {
@@ -721,6 +756,55 @@ func syncContactFromEmployee(tid uint64, employeeID uint64, emp *models.BackendE
return "updated"
}
// getEmployeeFileForContact 获取员工人事档案数据(用于通讯录同步)
// 返回档案中与通讯录相关的字段
func getEmployeeFileForContact(employeeID uint64) struct {
WorkEmail string
HouseholdAddress string
CurrentAddress string
EmergencyContact string
EmergencyPhone string
EmergencyRelation string
EmploymentStatus int8
} {
var file models.BackendEmployeeFile
err := models.Orm.QueryTable(new(models.BackendEmployeeFile)).
Filter("employee_id", employeeID).
Filter("is_deleted", 0).
One(&file)
if err != nil {
// 档案不存在,返回空值
return struct {
WorkEmail string
HouseholdAddress string
CurrentAddress string
EmergencyContact string
EmergencyPhone string
EmergencyRelation string
EmploymentStatus int8
}{}
}
return struct {
WorkEmail string
HouseholdAddress string
CurrentAddress string
EmergencyContact string
EmergencyPhone string
EmergencyRelation string
EmploymentStatus int8
}{
WorkEmail: file.WorkEmail,
HouseholdAddress: file.HouseholdAddress,
CurrentAddress: file.CurrentAddress,
EmergencyContact: file.EmergencyContact,
EmergencyPhone: file.EmergencyPhone,
EmergencyRelation: file.EmergencyRelation,
EmploymentStatus: file.EmploymentStatus,
}
}
// --- Helper functions (package-level, shared with ERP controller) ---
func contactDerefString(v *string) string {
@@ -879,3 +963,17 @@ func (c *BackendErpContactController) contactJsonError(code int, msg string) {
c.Data["json"] = map[string]interface{}{"code": code, "msg": msg}
_ = c.ServeJSON()
}
// contactFirstNonEmptyPtr 按优先级取第一个非空字符串(去空格后判断),全部为空时返回 nil。
// 用于通讯录"档案字段 > 员工字段"的取值优先级,避免用空字符串覆盖已有数据。
func contactFirstNonEmptyPtr(values ...*string) *string {
for _, v := range values {
if v == nil {
continue
}
if s := strings.TrimSpace(*v); s != "" {
return &s
}
}
return nil
}