material_manage_system/internal/repository/organization_access.go
文睿 张 970ba5ccf7 feat: 组织列表为每家公司补充仓库摘要
组织初始化接口 GET /api/auth/organizations 现在为每项授权公司附带
warehouse 摘要({id, name, status, location: {id, name, status} | null} | null),
结构与切换接口一致,供普通用户在公司切换器直接展示,无需放开管理员仓库配置接口。

- model:新增 WarehouseSummary/LocationSummary/OrganizationSummary,
  OrganizationAccess 每项改为内嵌公司资料再加 warehouse 摘要。
- repository:新增 WarehouseRepository.Summary 复用切换接口的解析规则;
  ListAccessible 逐公司补齐摘要,仓库或库位配置不唯一时报错而非随意取第一条,
  无仓库、无库位或已停用如实返回,不回退旧公司数据。
- service:Switch 复用 List 已解析的摘要,响应格式保持不变。
- 测试:新增服务层 sqlmock 用例与仓储层 MySQL 集成用例,
  覆盖启用唯一仓库加启用库位、无仓库、仓库停用三类。
2026-10-09 10:43:35 +08:00

62 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repository
import (
"database/sql"
"github.com/wmms/wmms/internal/model"
"github.com/wmms/wmms/pkg/database"
)
// Authorization is evaluated from current database grants and enabled roles on
// every call, so role changes and grant revocation do not wait for JWT expiry.
func (r *OrganizationRepository) ListAccessible(userID int64) (*model.OrganizationAccess, error) {
rows, err := database.DB.Query(`SELECT o.id,o.code,o.name,o.status,o.default_timezone,o.created_at,o.updated_at,
COALESCE(g.is_default,0) FROM organizations o
LEFT JOIN user_organizations g ON g.organization_id=o.id AND g.user_id=?
WHERE o.status='active' AND (g.user_id IS NOT NULL OR EXISTS(
SELECT 1 FROM user_roles ur JOIN roles r ON r.id=ur.role_id
WHERE ur.user_id=? AND r.code='super_admin' AND r.enabled=1)) ORDER BY o.id`, userID, userID)
if err != nil {
return nil, err
}
defer rows.Close()
access := &model.OrganizationAccess{Organizations: make([]model.OrganizationSummary, 0)}
for rows.Next() {
var item model.OrganizationSummary
var isDefault bool
if err := rows.Scan(&item.ID, &item.Code, &item.Name, &item.Status, &item.DefaultTimezone, &item.CreatedAt, &item.UpdatedAt, &isDefault); err != nil {
return nil, err
}
access.Organizations = append(access.Organizations, item)
if isDefault {
id := item.ID
access.DefaultOrganizationID = &id
}
}
if err := rows.Err(); err != nil {
return nil, err
}
// 逐公司补齐仓库、库位摘要,供普通用户在公司切换器直接展示。查询复用
// WarehouseRepository;公司或库位配置不唯一时 Summary 会报错,不随意取第一条。
warehouses := NewWarehouseRepository()
for i := range access.Organizations {
summary, err := warehouses.Summary(access.Organizations[i].ID)
if err != nil {
return nil, err
}
access.Organizations[i].Warehouse = summary
}
return access, nil
}
func (r *OrganizationRepository) CanAccess(userID, organizationID int64) (bool, error) {
var allowed bool
err := database.DB.QueryRow(`SELECT EXISTS(SELECT 1 FROM organizations o WHERE o.id=? AND o.status='active'
AND (EXISTS(SELECT 1 FROM user_organizations g WHERE g.user_id=? AND g.organization_id=o.id)
OR EXISTS(SELECT 1 FROM user_roles ur JOIN roles r ON r.id=ur.role_id
WHERE ur.user_id=? AND r.code='super_admin' AND r.enabled=1)))`, organizationID, userID, userID).Scan(&allowed)
if err == sql.ErrNoRows {
return false, nil
}
return allowed, err
}