diff --git a/YLErpWeb/WebAPI/Controllers/TradeApprovalOAJobController.cs b/YLErpWeb/WebAPI/Controllers/TradeApprovalOAJobController.cs
new file mode 100644
index 00000000..ffda966e
--- /dev/null
+++ b/YLErpWeb/WebAPI/Controllers/TradeApprovalOAJobController.cs
@@ -0,0 +1,25 @@
+using System.Text.Json;
+using YLErp.Helpers;
+using YLErp.Model;
+using YLErp.Modules.TradeModule;
+
+namespace YLErp.Web.WebAPI.Controllers
+{
+ /// 国联民生衍生品移动审批轮询入口,由 PowerJob 等外部调度器调用。
+ [ApiController]
+ public class TradeApprovalOAJobController : ControllerBase
+ {
+ [HttpPost]
+ [Route("api/job/tradeApproval/syncOAStatus")]
+ public JsonResult SyncOAStatus([FromQuery] string requestId = null)
+ {
+ var configKey = AppManager.GetConfiguration()["JobConfig:ApiKey"];
+ if (!string.IsNullOrEmpty(configKey) && Request.Headers["X-Job-Api-Key"].FirstOrDefault() != configKey)
+ return new JsonResult(new { code = 401, msg = "Invalid API Key" });
+
+ var user = new OptUserInfo(0, "系统移动审批同步", OptUserFrom.System);
+ var messages = new TradeApprovalOAService(user).SyncPendingStatuses(requestId);
+ return new JsonResult(new { code = 0, data = new { total = messages.Count, details = messages } }, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
+ }
+ }
+}
diff --git a/YLErpWeb/WebAPI/Controllers/TradeApprovalOaMockController.cs b/YLErpWeb/WebAPI/Controllers/TradeApprovalOaMockController.cs
new file mode 100644
index 00000000..e5bafa15
--- /dev/null
+++ b/YLErpWeb/WebAPI/Controllers/TradeApprovalOaMockController.cs
@@ -0,0 +1,110 @@
+using System.Collections.Concurrent;
+using System.Text.Json;
+using YLErp.Helpers;
+
+namespace YLErp.Web.WebAPI.Controllers
+{
+ ///
+ /// 仅供本地开发验证收益互换移动审批调用链的 OA 模拟接口。
+ ///
+ [ApiController]
+ [Route("api/mock/tradeApprovalOa")]
+ public class TradeApprovalOaMockController : ControllerBase
+ {
+ private static readonly ConcurrentDictionary Flows = new();
+ private static readonly JsonSerializerOptions JsonOptions = new()
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ };
+
+ [HttpPost("createOaFlow")]
+ public IActionResult CreateOaFlow([FromBody] JsonElement payload)
+ {
+ if (!IsEnabled()) return NotFound();
+
+ var requestId = "mock-" + Guid.NewGuid().ToString("N");
+ Flows[requestId] = new MockOaFlow { RequestId = requestId };
+ return Json(new { code = 0, data = new { msg = string.Empty, requestid = requestId }, status = "SUCCESS" });
+ }
+
+ [HttpGet("getRequestData")]
+ public IActionResult GetRequestData([FromQuery] string requestId)
+ {
+ if (!IsEnabled()) return NotFound();
+
+ if (!Flows.TryGetValue(requestId ?? string.Empty, out var flow))
+ return Json(new { code = 0, data = Array.Empty