R52 A1-A6:完成 AP 运营深度操作

- AP 概览页 Tab 工作台化(待付款/异常付款/风险标记/对账异常)
- AP 导出端点支持 CSV/XLSX 与筛选
- 风险标记 resolve/dismiss 支持说明审计留痕
- 支付批次深链、高风险复核提示、对账确认权限收口
- OpenClaw 浏览器级回归全部通过
这个提交包含在:
2026-04-07 09:37:17 +08:00
父节点 02c01dadb9
当前提交 64d9ded024
共修改 9 个文件,包含 2325 行新增和 63 行删除
+22
查看文件
@@ -12,6 +12,9 @@ const QUERY_SCHEMA = z.object({
status: z.enum(["open", "resolved", "dismissed"]).optional(),
source: z.string().trim().max(32).optional(),
});
const ACTION_BODY_SCHEMA = z.object({
note: z.string().trim().max(500).nullable().optional(),
}).strict();
function buildValidationError(details, statusCode = 400) {
return new AppError({
@@ -58,6 +61,22 @@ function parseId(value) {
return parsed.data;
}
function parseBody(body) {
const parsed = ACTION_BODY_SCHEMA.safeParse(body ?? {});
if (!parsed.success) {
throw buildValidationError(parsed.error.flatten());
}
return parsed.data;
}
function normalizeNote(value) {
if (value == null) {
return null;
}
const trimmed = String(value).trim();
return trimmed || null;
}
async function riskFlagsRoutes(fastify) {
fastify.get("/risk-flags", { preHandler: fastify.authenticate }, async (request, reply) => {
ensureListPermission(fastify, request);
@@ -78,6 +97,8 @@ async function riskFlagsRoutes(fastify) {
fastify.post(`/risk-flags/:id/${action}`, { preHandler: fastify.authenticate }, async (request, reply) => {
ensureManagePermission(fastify, request);
const riskFlagId = parseId(request.params?.id);
const body = parseBody(request.body);
const note = normalizeNote(body.note);
const current = await fastify.prisma.entityRiskFlag.findUnique({ where: { id: riskFlagId } });
if (!current) {
throw new AppError({ code: "SYS_404", message: "Not Found", statusCode: 404 });
@@ -105,6 +126,7 @@ async function riskFlagsRoutes(fastify) {
entityType: updated.entity_type,
entityId: updated.entity_id,
riskCode: updated.risk_code,
note,
},
}),
});