147 lines
4.1 KiB
Go
147 lines
4.1 KiB
Go
package service
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
|
|
"github.com/wmms/wmms/internal/model"
|
|
"github.com/wmms/wmms/internal/repository"
|
|
"github.com/wmms/wmms/pkg/database"
|
|
)
|
|
|
|
var ErrInvalidOrganizationID = errors.New("请选择有效的公司")
|
|
var ErrOrganizationInaccessible = errors.New("当前公司不可访问")
|
|
|
|
type OrganizationAccessService struct {
|
|
repo *repository.OrganizationRepository
|
|
}
|
|
|
|
func NewOrganizationAccessService() *OrganizationAccessService {
|
|
return &OrganizationAccessService{repo: repository.NewOrganizationRepository()}
|
|
}
|
|
|
|
// EffectiveRoles reloads the principal for every authenticated request. A JWT
|
|
// authenticates identity; its role snapshot never grants current privileges.
|
|
func (s *OrganizationAccessService) EffectiveRoles(userID int64) ([]string, error) {
|
|
rows, err := database.DB.Query(`SELECT u.id, r.code FROM users u
|
|
LEFT JOIN user_roles ur ON ur.user_id=u.id
|
|
LEFT JOIN roles r ON r.id=ur.role_id AND r.enabled=1
|
|
WHERE u.id=? ORDER BY r.id`, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
roles := []string{}
|
|
found := false
|
|
for rows.Next() {
|
|
var id int64
|
|
var code sql.NullString
|
|
if err := rows.Scan(&id, &code); err != nil {
|
|
return nil, err
|
|
}
|
|
found = true
|
|
if code.Valid {
|
|
roles = append(roles, code.String)
|
|
}
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
if !found {
|
|
return nil, sql.ErrNoRows
|
|
}
|
|
return roles, nil
|
|
}
|
|
|
|
func (s *OrganizationAccessService) List(userID int64) (*model.OrganizationAccess, error) {
|
|
return s.repo.ListAccessible(userID)
|
|
}
|
|
|
|
func (s *OrganizationAccessService) Validate(userID, organizationID int64) error {
|
|
if organizationID <= 0 {
|
|
return ErrInvalidOrganizationID
|
|
}
|
|
allowed, err := s.repo.CanAccess(userID, organizationID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !allowed {
|
|
return ErrOrganizationInaccessible
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type LocationContext struct {
|
|
ID int64 `json:"id"`
|
|
Name string `json:"name"`
|
|
Status string `json:"status"`
|
|
}
|
|
type WarehouseContext struct {
|
|
ID int64 `json:"id"`
|
|
Name string `json:"name"`
|
|
Status string `json:"status"`
|
|
Location *LocationContext `json:"location"`
|
|
}
|
|
type OrganizationContext struct {
|
|
OrganizationID int64 `json:"organization_id"`
|
|
Organization model.Organization `json:"organization"`
|
|
Warehouse *WarehouseContext `json:"warehouse"`
|
|
}
|
|
|
|
// Switch validates the target independently of the client-reported previous
|
|
// context. It records an audit, without storing a server-wide selected company.
|
|
func (s *OrganizationAccessService) Switch(userID, organizationID, previousID int64) (*OrganizationContext, error) {
|
|
if organizationID <= 0 {
|
|
return nil, ErrInvalidOrganizationID
|
|
}
|
|
access, err := s.List(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var target *model.Organization
|
|
for i := range access.Organizations {
|
|
if access.Organizations[i].ID == organizationID {
|
|
target = &access.Organizations[i]
|
|
break
|
|
}
|
|
}
|
|
if target == nil {
|
|
return nil, ErrOrganizationInaccessible
|
|
}
|
|
result := &OrganizationContext{OrganizationID: organizationID, Organization: *target}
|
|
warehouses, err := repository.NewWarehouseRepository().List(organizationID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(warehouses) > 1 {
|
|
return nil, errors.New("公司仓库配置不唯一")
|
|
}
|
|
if len(warehouses) == 1 {
|
|
w := warehouses[0]
|
|
if len(w.Locations) > 1 {
|
|
return nil, errors.New("公司库位配置不唯一")
|
|
}
|
|
result.Warehouse = &WarehouseContext{ID: w.ID, Name: w.Name, Status: w.Status}
|
|
if len(w.Locations) == 1 {
|
|
l := w.Locations[0]
|
|
result.Warehouse.Location = &LocationContext{ID: l.ID, Name: l.Name, Status: l.Status}
|
|
}
|
|
}
|
|
oldValue, err := json.Marshal(map[string]int64{"organization_id": previousID})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
newValue, err := json.Marshal(map[string]int64{"organization_id": organizationID})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = repository.NewOrganizationAuditRepository().Create(&model.OrganizationAuditLog{
|
|
ActorUserID: userID, OrganizationID: &organizationID, Action: "organization_context_switched", OldValue: oldValue, NewValue: newValue,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|