批量优化
This commit is contained in:
1 parent
438145cae7
commit
c925b198d1
50 files changed
+5716
-291
No files matched your search
@@ -12,6 +12,7 @@ import (
|
||||
"server/pkg/jwtutil"
|
||||
"server/pkg/passwordutil"
|
||||
"server/services"
|
||||
authsvc "server/services/auth"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
@@ -56,11 +57,18 @@ type backendTenantUserPayload struct {
|
||||
IsDefault *int8 `json:"is_default"`
|
||||
Status *int8 `json:"status"`
|
||||
Remark *string `json:"remark"`
|
||||
// IdentityID + VerifyPassword:绑定已有认证身份时使用。
|
||||
// 归并键(手机/邮箱/账号)命中已有身份时,接口返回 409 要求验证;
|
||||
// 前端补上 identity_id 与该账号的登录密码(verify_password)重新提交,
|
||||
// 验证通过只建企业绑定,不新建身份、不覆盖原密码。
|
||||
IdentityID uint64 `json:"identity_id"`
|
||||
VerifyPassword string `json:"verify_password"`
|
||||
}
|
||||
|
||||
type backendChangePasswordPayload struct {
|
||||
ID uint64 `json:"id"`
|
||||
Password string `json:"password"`
|
||||
By string `json:"by"`
|
||||
}
|
||||
|
||||
func formatBackendBirth(birth *string) *string {
|
||||
@@ -187,7 +195,12 @@ func (c *BackendAdminUserController) parseTenantUserPayload() (backendTenantUser
|
||||
// yz_system_tenant_user.uid 仍是老 uid(老表不在 uid 全量迁移范围内),
|
||||
// 因此必须先换算再按 uid 查;先按主键 id 查会把 identity_id 命中的是
|
||||
// 主键恰好相同的**别人**的记录(个人中心显示他人资料就是这么来的)。
|
||||
func findBackendTenantUser(idOrUid uint64, jwtTid uint64) (*models.SystemTenantUser, error) {
|
||||
//
|
||||
// byID=true 表示调用方明确传入的是 yz_system_tenant_user 主键
|
||||
// (用户管理页的行数据,列表接口返回的 id 就是主键),
|
||||
// 此时只按主键精确查询,禁止 identity_id/uid 换算——否则主键值
|
||||
// 恰好等于别人老 uid 时会查到**别人**的记录(编辑李增炎却回显李志强)。
|
||||
func findBackendTenantUser(idOrUid uint64, jwtTid uint64, byID bool) (*models.SystemTenantUser, error) {
|
||||
if idOrUid == 0 {
|
||||
return nil, errors.New("用户ID不能为空")
|
||||
}
|
||||
@@ -201,6 +214,14 @@ func findBackendTenantUser(idOrUid uint64, jwtTid uint64) (*models.SystemTenantU
|
||||
return qs
|
||||
}
|
||||
|
||||
if byID {
|
||||
var row models.SystemTenantUser
|
||||
if err := base().Filter("id", idOrUid).One(&row); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &row, nil
|
||||
}
|
||||
|
||||
// 1) 当作 identity_id:换算成本租户老 uid 后匹配
|
||||
var row models.SystemTenantUser
|
||||
if uid := services.ResolveTenantUID(jwtTid, idOrUid); uid > 0 {
|
||||
@@ -323,14 +344,18 @@ func (c *BackendAdminUserController) GetTenantUsers() {
|
||||
}
|
||||
|
||||
// GetUserInfo 获取后台租户用户详情
|
||||
// GET /backend/getUserInfo/:id
|
||||
// GET /backend/getUserInfo/:id[?by=id]
|
||||
// by=id:前端明确传主键(用户管理页),只按主键查询;
|
||||
// 缺省时兼容 identity_id / 老 uid(个人中心等场景)。
|
||||
func (c *BackendAdminUserController) GetUserInfo() {
|
||||
jwtUid, jwtTid := c.getJWTUidTid()
|
||||
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
|
||||
if id == 0 {
|
||||
byID := c.GetString("by") == "id"
|
||||
|
||||
if id == 0 && !byID {
|
||||
id = jwtUid
|
||||
}
|
||||
|
||||
@@ -340,7 +365,7 @@ func (c *BackendAdminUserController) GetUserInfo() {
|
||||
return
|
||||
}
|
||||
|
||||
u, err := findBackendTenantUser(id, jwtTid)
|
||||
u, err := findBackendTenantUser(id, jwtTid, byID)
|
||||
if err != nil || u == nil {
|
||||
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "后台用户信息不存在"}
|
||||
_ = c.ServeJSON()
|
||||
@@ -389,16 +414,75 @@ func (c *BackendAdminUserController) AddUser() {
|
||||
account := strings.TrimSpace(*p.Account)
|
||||
p.Account = &account
|
||||
|
||||
hashed, err := passwordutil.Hash(strings.TrimSpace(*p.Password))
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{"code": 400, "msg": err.Error()}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
p.Password = &hashed
|
||||
// ── 统一认证:一人一套账号密码,跨企业只建绑定 ─────────────────────────
|
||||
// 分支①:前端携带 identity_id(409 验证后的二次提交)——
|
||||
// 验证该身份的登录密码,通过后只建企业绑定,不新建身份、不覆盖原密码。
|
||||
if p.IdentityID > 0 {
|
||||
if strings.TrimSpace(p.VerifyPassword) == "" {
|
||||
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "请输入该账号在原企业的登录密码以验证身份"}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
identity, err := authsvc.GetIdentityByID(p.IdentityID)
|
||||
if err != nil || identity == nil {
|
||||
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "认证身份不存在"}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
if !authsvc.VerifyIdentityPassword(identity, strings.TrimSpace(p.VerifyPassword)) {
|
||||
c.Data["json"] = map[string]interface{}{"code": 401, "msg": "密码不正确,无法确认是同一账号"}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
// uid 统一为 identity_id,与已全量迁移的业务表口径一致
|
||||
p.Uid = identity.ID
|
||||
// 老表镜像身份层现有密码哈希(登录仍以认证中心为准)
|
||||
if identity.PasswordHash != nil && *identity.PasswordHash != "" {
|
||||
h := *identity.PasswordHash
|
||||
p.Password = &h
|
||||
} else {
|
||||
// 身份层无密码(历史数据),仅镜像到老表,不影响统一认证
|
||||
hashed, hErr := passwordutil.Hash(strings.TrimSpace(*p.Password))
|
||||
if hErr != nil {
|
||||
c.Data["json"] = map[string]interface{}{"code": 400, "msg": hErr.Error()}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
p.Password = &hashed
|
||||
}
|
||||
} else if p.Uid == 0 {
|
||||
// 分支②:归并键(手机 > 邮箱 > 账号)命中已有身份 → 不覆盖,
|
||||
// 返回 409 让前端提示「该账号已在哪些企业存在」,
|
||||
// 用户输入原企业登录密码验证本人后,携 identity_id 重新提交(走分支①)。
|
||||
if existed, hit := authsvc.LocateIdentityByKey(account, strOrEmpty(p.Phone), strOrEmpty(p.Email)); hit {
|
||||
hints := make([]map[string]interface{}, 0)
|
||||
if tenants, err := authsvc.ListTenantOptions(existed.ID); err == nil {
|
||||
for _, t := range tenants {
|
||||
hints = append(hints, map[string]interface{}{"tid": t.Tid, "tenant_name": t.TenantName})
|
||||
}
|
||||
}
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 409,
|
||||
"msg": "该账号已在其他企业存在,请输入原企业登录密码验证身份后绑定本企业",
|
||||
"data": map[string]interface{}{
|
||||
"need_verify": true,
|
||||
"identity_id": existed.ID,
|
||||
"tenants": hints,
|
||||
},
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 分支③:全新身份 —— password 必填,先建认证身份再拿 identity_id 当 uid
|
||||
hashed, err := passwordutil.Hash(strings.TrimSpace(*p.Password))
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{"code": 400, "msg": err.Error()}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
p.Password = &hashed
|
||||
|
||||
if p.Uid == 0 {
|
||||
// 统一走 uid:优先用认证中心 identity_id,与已全量迁移的业务表口径一致
|
||||
uid, err := ensureTenantUIDForNewUser(
|
||||
p.Tid,
|
||||
strOrEmpty(p.Account),
|
||||
@@ -478,10 +562,11 @@ func (c *BackendAdminUserController) AddUser() {
|
||||
}
|
||||
}
|
||||
|
||||
// 双写:同步到统一认证中心(读回最新记录,确保 group_id 已落库)
|
||||
// 双写:同步到统一认证中心(读回最新记录,确保 group_id 已落库);
|
||||
// 身份若已存在,密码以身份层为准,不同步(避免覆盖)
|
||||
var created models.SystemTenantUser
|
||||
if e := models.Orm.QueryTable(new(models.SystemTenantUser)).Filter("id", id).One(&created); e == nil {
|
||||
syncTenantUserToAuth(&created)
|
||||
syncTenantUserToAuth(&created, false)
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
@@ -511,7 +596,7 @@ func (c *BackendAdminUserController) EditUser() {
|
||||
|
||||
_, jwtTid := c.getJWTUidTid()
|
||||
|
||||
row, err := findBackendTenantUser(id, jwtTid)
|
||||
row, err := findBackendTenantUser(id, jwtTid, c.GetString("by") == "id")
|
||||
if err != nil || row == nil {
|
||||
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "用户不存在"}
|
||||
_ = c.ServeJSON()
|
||||
@@ -606,10 +691,10 @@ func (c *BackendAdminUserController) EditUser() {
|
||||
_ = services.SetDefaultTenant(row.Uid, row.Tid)
|
||||
}
|
||||
|
||||
// 双写:同步到统一认证中心
|
||||
// 双写:同步到统一认证中心(编辑不改密码,不同步密码字段)
|
||||
var edited models.SystemTenantUser
|
||||
if e := models.Orm.QueryTable(new(models.SystemTenantUser)).Filter("id", row.ID).One(&edited); e == nil {
|
||||
syncTenantUserToAuth(&edited)
|
||||
syncTenantUserToAuth(&edited, false)
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success"}
|
||||
@@ -620,8 +705,8 @@ func (c *BackendAdminUserController) EditUser() {
|
||||
// DELETE /backend/deleteUser/:id
|
||||
//
|
||||
// 用户管理里的账号属于租户层面的登录凭证(yz_system_tenant_user),与员工
|
||||
//(yz_backend_employee)、人事档案(yz_backend_employee_file)、通讯录
|
||||
//(yz_backend_erp_contact)是三份不同用途的数据:账号只用于登录系统,员工与档案属于企业数据。
|
||||
// (yz_backend_employee)、人事档案(yz_backend_employee_file)、通讯录
|
||||
// (yz_backend_erp_contact)是三份不同用途的数据:账号只用于登录系统,员工与档案属于企业数据。
|
||||
// 因此这里只解除登录绑定,不做任何级联操作——删除账号不会影响员工档案和通讯录记录。
|
||||
// 员工侧的离职/禁用走 deleteEmployee,档案侧的离职走 employeefile/delete。
|
||||
func (c *BackendAdminUserController) DeleteUser() {
|
||||
@@ -636,7 +721,7 @@ func (c *BackendAdminUserController) DeleteUser() {
|
||||
|
||||
_, jwtTid := c.getJWTUidTid()
|
||||
|
||||
row, err := findBackendTenantUser(id, jwtTid)
|
||||
row, err := findBackendTenantUser(id, jwtTid, c.GetString("by") == "id")
|
||||
if err != nil || row == nil {
|
||||
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "用户不存在"}
|
||||
_ = c.ServeJSON()
|
||||
@@ -682,7 +767,7 @@ func (c *BackendAdminUserController) ChangePassword() {
|
||||
|
||||
_, jwtTid := c.getJWTUidTid()
|
||||
|
||||
row, err := findBackendTenantUser(p.ID, jwtTid)
|
||||
row, err := findBackendTenantUser(p.ID, jwtTid, p.By == "id")
|
||||
if err != nil || row == nil {
|
||||
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "用户不存在"}
|
||||
_ = c.ServeJSON()
|
||||
@@ -708,10 +793,10 @@ func (c *BackendAdminUserController) ChangePassword() {
|
||||
return
|
||||
}
|
||||
|
||||
// 双写:同步新密码到统一认证中心
|
||||
// 双写:同步新密码到统一认证中心(改密场景,显式允许更新身份层密码)
|
||||
var updated models.SystemTenantUser
|
||||
if e := models.Orm.QueryTable(new(models.SystemTenantUser)).Filter("id", row.ID).One(&updated); e == nil {
|
||||
syncTenantUserToAuth(&updated)
|
||||
syncTenantUserToAuth(&updated, true)
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "修改成功"}
|
||||
|
||||
Reference in new issue
Block a user