using Admin.NET.Core.Service;
using Admin.NET.Plugin.AiDOP.Infrastructure;
namespace Admin.NET.Plugin.AiDOP.Controllers.S0;
///
/// S0 自有的**当前租户只读组织查询**(P1-2)。
///
/// 目的:让 S0 页面不再依赖平台 /api/sysOrg/list。
/// 平台该路由已登记为按钮权限 sysOrg:list(SysMenu Id=1300200030201),
/// 普通 S0 业务角色为了填一个隐藏的公司/工厂归属字段而被迫持有平台机构管理权限,
/// 并因菜单祖先补全在侧边栏暴露「系统管理 / 机构管理」入口。
/// 本路由 s0:common:orgs 未登记为按钮权限,走 JwtHandler「未登记即放行」分支,
/// 普通业务角色无需任何平台机构权限即可读取本租户组织。
///
/// 边界(只读 + 只当前租户 + 最小字段):
/// - 租户来自 (JWT claim),不接受 payload;
/// - 复用 ,与平台口径**完全一致**(含 GetUserOrgIdList
/// 数据权限收窄与 SysOrg 全局租户过滤),不新写一套查询逻辑、不放宽可见范围;
/// - 只返回 id / pid / name / code / type 五个字段,不暴露机构管理的任何写能力;
/// - 额外对结果再做一次当前租户断言,作为纵深防御。
///
/// 返回一律为**平铺列表**:平台 GetList 在不带 Type 条件时返回树,本接口统一拍平,
/// 使调用方(loadOrgList)无需再关心树/平铺两种形态。
///
[ApiController]
[Route("api/s0/common/orgs")]
[NonUnify]
public class AdoS0OrgsController : ControllerBase
{
private readonly SysOrgService _sysOrgService;
public AdoS0OrgsController(SysOrgService sysOrgService)
{
_sysOrgService = sysOrgService;
}
///
/// 查询当前租户的组织列表。 为 SysOrg.Type(S0 约定 201=公司、501=工厂,
/// 501 沿用 S0 legacy 兼容语义,见 AdoS0OrgScope 注释);不传则返回本租户全部组织。
///
[HttpGet]
public async Task GetListAsync([FromQuery] string? type)
{
if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
var raw = await _sysOrgService.GetList(new OrgInput { Id = 0, Type = type });
var flat = new List();
Flatten(raw, flat);
var result = flat
.Where(x => x.TenantId == tenantId) // 纵深防御:平台侧已按租户过滤,这里再断言一次
.GroupBy(x => x.Id)
.Select(g => g.First())
.OrderBy(x => x.OrderNo).ThenBy(x => x.Id)
.Select(x => new
{
id = x.Id.ToString(), // 雪花 Id 超 JS number 精度,一律以 string 下发
pid = x.Pid.ToString(),
name = x.Name,
code = x.Code,
type = x.Type,
})
.ToList();
return Ok(result);
}
/// 把可能的树形结果拍平(平台 GetList 不带条件时返回树,子节点在 Children 内)。
private static void Flatten(List? nodes, List sink)
{
if (nodes == null) return;
foreach (var n in nodes)
{
sink.Add(n);
Flatten(n.Children, sink);
}
}
}