feat(server): P5 基础能力补齐(组织/报销/代办/认证安全)

- 组织/人员/委派对齐:费用类型、部门、员工别名、配置只读与回归脚本(F2/F3/F26/F27/F29/F31/F40)

- 报销单 PATCH/DELETE 与 items CRUD(事务内重算 total_amount)(F4/F5)

- 行政代提交/代上传后端校验与审计(F7/F8)

- 认证安全基线:首次登录改密、失败锁定、弱密码拦截与回归脚本(F36/F37/F39/F41)
这个提交包含在:
2026-02-09 16:20:31 +08:00
父节点 a8080b05f8
当前提交 f755ff0dda
共修改 27 个文件,包含 3572 行新增和 97 行删除
+11 -5
查看文件
@@ -136,15 +136,22 @@ NODE
}
create_tmp_non_dedup_index() {
DATABASE_URL="$DATABASE_URL" node <<'NODE'
local reimbursement_id="$1"
DATABASE_URL="$DATABASE_URL" REIMBURSEMENT_ID="$reimbursement_id" node <<'NODE'
const { createPrismaClient } = require('./src/prismaClient');
(async () => {
const reimbursementId = process.env.REIMBURSEMENT_ID;
if (!reimbursementId) {
throw new Error('REIMBURSEMENT_ID is required');
}
const { prisma, pool } = createPrismaClient();
await prisma.$connect();
try {
await prisma.$executeRawUnsafe('DROP INDEX IF EXISTS "tmp_unique_linked_reim"');
await prisma.$executeRawUnsafe(
'CREATE UNIQUE INDEX IF NOT EXISTS tmp_unique_linked_reim ON "invoice_pool" ("linked_reimbursement_id") WHERE "linked_reimbursement_id" IS NOT NULL',
`CREATE UNIQUE INDEX "tmp_unique_linked_reim" ON "invoice_pool" ("linked_reimbursement_id") WHERE "linked_reimbursement_id" = '${reimbursementId}'`,
);
} finally {
await prisma.$disconnect();
@@ -247,9 +254,6 @@ assert_status "$status" "409" "code+number dedup bind" "$TMP_DIR/bD.json"
assert_error_code "$TMP_DIR/bD.json" "VALID_003"
echo "[4/7] Non-dedup P2002 should not map to VALID_003"
create_tmp_non_dedup_index
TMP_INDEX_CREATED=1
cat > "$TMP_DIR/non-dedup-1.pdf" <<PDF
%PDF-1.4
non-dedup-1-$RUN_SUFFIX
@@ -260,6 +264,8 @@ non-dedup-2-$RUN_SUFFIX
PDF
RID_E="$(create_reimbursement "$TMP_DIR/rE.json")"
create_tmp_non_dedup_index "$RID_E"
TMP_INDEX_CREATED=1
status="$(upload_file "$TMP_DIR/non-dedup-1.pdf" "$TMP_DIR/uE1.json")"
assert_status "$status" "200" "upload non-dedup-1" "$TMP_DIR/uE1.json"
IID_E1="$(jq -er '.data.invoicePoolId' "$TMP_DIR/uE1.json")"