using Admin.NET.Plugin.AiDOP.Dto.S0.Manufacturing;
using Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Manufacturing;
///
/// 工单控制参数设置(WorkOrdControl):单记录配置表单。
///
/// 业务模型(S0-WORKORD-CONTROL-TENANT-UX-FIX):**每个租户一套配置**。
/// - tenant_id 是唯一隔离边界;Domain 只是历史兼容业务字段,不承担可见性/安全职责。
/// - GET 不要求传 Domain:不传即返回当前租户的配置,页面打开即可自动回填。
/// - Upsert 按 Id 定位时必须先 tenant scope;Id 属于其他租户一律 404,绝不更新。
/// - 单配置约束:未命中 Id 时按租户定位既有记录,不会因 Domain 不同而无限新建。
///
[ApiController]
[Route("api/s0/manufacturing/work-ord-control")]
[NonUnify]
public class AdoS0WorkOrdControlController : ControllerBase
{
private readonly SqlSugarRepository _rep;
public AdoS0WorkOrdControlController(SqlSugarRepository rep)
{
_rep = rep;
}
[HttpGet]
public async Task GetOneAsync([FromQuery] AdoS0WorkOrdControlGetDto q)
{
if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
// 租户优先:Domain 仅作可选业务筛选,留空即返回当前租户配置(页面无需先手输工厂域)。
var domain = q.Domain?.Trim();
var item = await _rep.ScopedTo(tenantId)
.WhereIF(!string.IsNullOrWhiteSpace(domain), x => x.Domain == domain)
.OrderBy(x => x.Id)
.FirstAsync();
return Ok(item);
}
[HttpPost("upsert")]
public async Task UpsertAsync([FromBody] AdoS0WorkOrdControlUpsertDto dto)
{
if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
var now = DateTime.Now;
var domain = dto.Domain?.Trim();
AdoS0WorkOrdControl? entity = null;
if (dto.Id is > 0)
{
// P0:先 tenant scope 再按 Id 命中。Id 属于其他租户时查不到 → 404,绝不落到 Update 分支。
entity = await _rep.ScopedTo(tenantId)
.Where(x => x.Id == dto.Id!.Value)
.FirstAsync();
if (entity == null) return NotFound();
}
// 单配置约束:一个租户最多一条当前配置,不因 Domain 不同而无限新建。
entity ??= await _rep.ScopedTo(tenantId)
.OrderBy(x => x.Id)
.FirstAsync();
if (entity == null)
{
entity = new AdoS0WorkOrdControl
{
TenantId = tenantId,
// Domain 可空:留空存空串(列非空),不猜 8010 / S0-F001 等历史值。
Domain = domain ?? string.Empty,
EnteringPer = dto.EnteringPer,
PaintingPer = dto.PaintingPer,
IssuedInterval = dto.IssuedInterval,
PostVarsatSFC = dto.PostVarsatSFC,
IsIssued = dto.IsIssued,
IsPrintRejectBarcode = dto.IsPrintRejectBarcode,
IsPrintScrapBarcode = dto.IsPrintScrapBarcode,
IsQualityConfirm = dto.IsQualityConfirm,
IsActive = dto.IsActive,
IsConfirm = dto.IsConfirm,
CreateUser = dto.CreateUser ?? dto.UpdateUser,
CreateTime = dto.CreateTime ?? dto.UpdateTime ?? now,
UpdateUser = dto.UpdateUser,
UpdateTime = dto.UpdateTime ?? now
};
await _rep.AsInsertable(entity).ExecuteReturnEntityAsync();
return Ok(entity);
}
// 保值:dto 未传 Domain 时不得抹掉既有历史值。
if (!string.IsNullOrWhiteSpace(domain)) entity.Domain = domain;
entity.EnteringPer = dto.EnteringPer;
entity.PaintingPer = dto.PaintingPer;
entity.IssuedInterval = dto.IssuedInterval;
entity.PostVarsatSFC = dto.PostVarsatSFC;
entity.IsIssued = dto.IsIssued;
entity.IsPrintRejectBarcode = dto.IsPrintRejectBarcode;
entity.IsPrintScrapBarcode = dto.IsPrintScrapBarcode;
entity.IsQualityConfirm = dto.IsQualityConfirm;
entity.IsActive = dto.IsActive;
entity.IsConfirm = dto.IsConfirm;
entity.UpdateUser = dto.UpdateUser;
entity.UpdateTime = dto.UpdateTime ?? now;
await _rep.AsUpdateable(entity).ExecuteCommandAsync();
return Ok(entity);
}
}