Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -27,11 +27,22 @@ type Row struct {
|
||||
}
|
||||
|
||||
type RoleDeptPermissionSummary struct {
|
||||
RoleID int `json:"role_id"`
|
||||
RoleTitle string `json:"role_title"`
|
||||
DepartmentCode string `json:"department_code"`
|
||||
DepartmentTitle string `json:"department_title"`
|
||||
ModuleFlags map[string]bool `json:"module_flags"`
|
||||
RoleID int `json:"role_id"`
|
||||
RoleTitle string `json:"role_title"`
|
||||
DepartmentCode string `json:"department_code"`
|
||||
DepartmentTitle string `json:"department_title"`
|
||||
ModuleFlags map[string]bool `json:"module_flags"`
|
||||
Members []RoleDeptMember `json:"members"`
|
||||
}
|
||||
|
||||
type RoleDeptMember struct {
|
||||
ID int64 `json:"id"`
|
||||
FullName string `json:"full_name"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
type AddRoleDeptMemberPayload struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
type ModuleLookupOption struct {
|
||||
@@ -132,12 +143,14 @@ func (h *RoleDepartmentPermissionHandler) List(w http.ResponseWriter, r *http.Re
|
||||
for rows.Next() {
|
||||
var item RoleDeptPermissionSummary
|
||||
var rawFlags []byte
|
||||
var rawMembers []byte
|
||||
if err := rows.Scan(
|
||||
&item.RoleID,
|
||||
&item.RoleTitle,
|
||||
&item.DepartmentCode,
|
||||
&item.DepartmentTitle,
|
||||
&rawFlags,
|
||||
&rawMembers,
|
||||
); err != nil {
|
||||
http.Error(w, "scan error", http.StatusInternalServerError)
|
||||
return
|
||||
@@ -150,6 +163,13 @@ func (h *RoleDepartmentPermissionHandler) List(w http.ResponseWriter, r *http.Re
|
||||
return
|
||||
}
|
||||
}
|
||||
item.Members = make([]RoleDeptMember, 0)
|
||||
if len(rawMembers) > 0 {
|
||||
if err := json.Unmarshal(rawMembers, &item.Members); err != nil {
|
||||
http.Error(w, "members parse error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
|
||||
@@ -294,6 +314,172 @@ func (h *RoleDepartmentPermissionHandler) Save(w http.ResponseWriter, r *http.Re
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
_ = json.NewEncoder(w).Encode(map[string]bool{"success": true})
|
||||
}
|
||||
|
||||
func (h *RoleDepartmentPermissionHandler) Members(w http.ResponseWriter, r *http.Request) {
|
||||
claims, ok := auth.GetClaimsFromContext(r.Context())
|
||||
if !ok || claims == nil {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
roleID, deptCode, ok := roleDepartmentFromRequest(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
members, err := listRoleDepartmentMembers(h.DB, roleID, deptCode)
|
||||
if err != nil {
|
||||
http.Error(w, "members query error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
_ = json.NewEncoder(w).Encode(members)
|
||||
}
|
||||
|
||||
func (h *RoleDepartmentPermissionHandler) AddMember(w http.ResponseWriter, r *http.Request) {
|
||||
claims, ok := auth.GetClaimsFromContext(r.Context())
|
||||
if !ok || claims == nil {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
roleID, deptCode, ok := roleDepartmentFromRequest(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var payload AddRoleDeptMemberPayload
|
||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil || payload.UserID <= 0 {
|
||||
http.Error(w, "invalid user_id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
tx, err := h.DB.BeginTx(r.Context(), nil)
|
||||
if err != nil {
|
||||
http.Error(w, "transaction start error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
var userExists, roleExists, departmentExists bool
|
||||
if err := tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM mk_dfusr WHERE id=$1 AND is_active=TRUE)`, payload.UserID).Scan(&userExists); err != nil {
|
||||
http.Error(w, "user lookup error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM dfrole WHERE id=$1)`, roleID).Scan(&roleExists); err != nil {
|
||||
http.Error(w, "role lookup error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM mk_dprt WHERE code=$1 AND is_active=TRUE)`, deptCode).Scan(&departmentExists); err != nil {
|
||||
http.Error(w, "department lookup error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if !userExists || !roleExists || !departmentExists {
|
||||
http.Error(w, "user, role or department not found", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`
|
||||
INSERT INTO dfrole_usr (dfusr_id, dfrole_id)
|
||||
VALUES ($1, $2)
|
||||
ON CONFLICT DO NOTHING
|
||||
`, payload.UserID, roleID); err != nil {
|
||||
http.Error(w, "user role insert error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`
|
||||
UPDATE dfusr_dprt ud
|
||||
SET is_active=TRUE
|
||||
FROM mk_dprt d
|
||||
WHERE ud.dfusr_id=$1
|
||||
AND ud.dprt_id=d.id
|
||||
AND d.code=$2
|
||||
`, payload.UserID, deptCode); err != nil {
|
||||
http.Error(w, "user department update error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`
|
||||
INSERT INTO dfusr_dprt (dfusr_id, dprt_id, is_active)
|
||||
SELECT $1, d.id, TRUE
|
||||
FROM mk_dprt d
|
||||
WHERE d.code=$2
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM dfusr_dprt ud
|
||||
WHERE ud.dfusr_id=$1
|
||||
AND ud.dprt_id=d.id
|
||||
)
|
||||
`, payload.UserID, deptCode); err != nil {
|
||||
http.Error(w, "user department insert error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
http.Error(w, "transaction commit error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
auditlog.Enqueue(r.Context(), auditlog.ActivityLog{
|
||||
ActionType: "role_department_member_add",
|
||||
ActionCategory: "role_permission",
|
||||
ActionTarget: fmt.Sprintf("/api/roles/%d/departments/%s/members", roleID, deptCode),
|
||||
Description: "user added to role+department group",
|
||||
Username: claims.Username,
|
||||
RoleCode: claims.RoleCode,
|
||||
DfUsrID: int64(claims.ID),
|
||||
ChangeAfter: map[string]any{
|
||||
"user_id": payload.UserID,
|
||||
"role_id": roleID,
|
||||
"department_code": deptCode,
|
||||
},
|
||||
IsSuccess: true,
|
||||
})
|
||||
|
||||
members, err := listRoleDepartmentMembers(h.DB, roleID, deptCode)
|
||||
if err != nil {
|
||||
http.Error(w, "members query error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
_ = json.NewEncoder(w).Encode(members)
|
||||
}
|
||||
|
||||
func roleDepartmentFromRequest(w http.ResponseWriter, r *http.Request) (int, string, bool) {
|
||||
vars := mux.Vars(r)
|
||||
roleID, err := strconv.Atoi(vars["roleId"])
|
||||
if err != nil || roleID <= 0 {
|
||||
http.Error(w, "invalid roleId", http.StatusBadRequest)
|
||||
return 0, "", false
|
||||
}
|
||||
deptCode := strings.TrimSpace(vars["deptCode"])
|
||||
if deptCode == "" {
|
||||
http.Error(w, "invalid deptCode", http.StatusBadRequest)
|
||||
return 0, "", false
|
||||
}
|
||||
return roleID, deptCode, true
|
||||
}
|
||||
|
||||
func listRoleDepartmentMembers(db *sql.DB, roleID int, deptCode string) ([]RoleDeptMember, error) {
|
||||
rows, err := db.Query(queries.ListRoleDepartmentMembers, roleID, deptCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
members := make([]RoleDeptMember, 0, 16)
|
||||
for rows.Next() {
|
||||
var member RoleDeptMember
|
||||
if err := rows.Scan(&member.ID, &member.FullName, &member.Username); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
members = append(members, member)
|
||||
}
|
||||
return members, rows.Err()
|
||||
}
|
||||
|
||||
func GetModuleLookupRoute(db *sql.DB) http.HandlerFunc {
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user