Auction 只有一个静态计算器,却最能说明行业算法商业化的风险:公式简单不代表法律适用简单。合同约定、未约定、拍卖标的类型、承担方和司法拍卖不是一个 5% 判断可以覆盖的。
1. 默认分档
默认表为:200万以下5%、200–1000万3%、1000–5000万2%、5000万以上1%,使用超额累进而不是整段单率。
var result = AuctionCommissionCalculator.CalcCommission( hammerPrice: 3_000_000m);
// 前 200 万按 5%,其余 100 万按 3%,不是 300 万整体按 3%。if (result.IsFailure) return result.Error;
// 当前结果 130000;源码没有返回每档明细或政策版本。Debug.Assert(result.Value == 130_000m);return result.Value;源码称该表是行业协会惯例,但没有具体文件、发布日期、适用拍卖类型或 hash。它不能被称为法定标准。
2. 自定义分档
overload 接收 ReadOnlySpan<TaxBracket> 并委托通用 Progressive。它只校验 hammerPrice 非负,不验证 brackets。空表会返回 0;乱序、负 rate、重复上限和未覆盖末档都可能生成无意义结果。
自定义政策必须先通过严格 Schedule.Create,并保存 source/version;不能把任意客户端数组直接交给计算器。
3. 固定比例 5% 限制
CalcCommissionByRate 拒绝负 price/rate,并对 rate>0.05 返回 Validation。随后 price×rate,两位 AwayFromZero。
《拍卖法》第五十六条首先允许委托人、买受人与拍卖人约定佣金比例;未约定且成交时,双方各可收不超过成交价5%;第五十七条对第九条特定物品买受人规定不超过5%。当前 API 没有场景参数,却把5%当所有固定约定的硬上限,可能拒绝法律语义不同的合同。
4. 司法拍卖边界
类注释提到网络司法拍卖另有规定,但没有司法拍卖专用方法、主体、费用或法源版本。调用者不能因为类名包含 Auction 就把默认表用于人民法院网络司法拍卖。
目标应拆成明确场景:CommercialAgreed、CommercialUnagreedSeller、CommercialUnagreedBuyer、Article9Buyer、JudicialAuction 等;每个 policy 独立版本化。
5. 结果明细
当前只返回 decimal,无法解释哪个区间、哪一方、哪条法源、何时生效。商业结果至少包含 HammerPrice、Currency、PayerRole、AuctionType、ContractTerm、每档 base/rate/amount、rounding、policy version/source 和 warnings。
var policy = await commissionPolicies.ResolveAsync( auctionType, payerRole, contractDate, cancellationToken);
// 缺政策或法源过期时失败关闭,不回退到匿名默认表。if (policy.IsFailure) return policy.Error;
// Breakdown 保留每档与法源证据,供合同审查和对账。return commissionEngine.Calculate(hammerPrice, policy.Value) .WithPolicyEvidence(policy.Value.Version, policy.Value.SourceHash);6. 当前测试事实
tests 下没有引用 Auction、Finance、HR 的任何计算器或方法。也没有 AOT publish smoke、public API snapshot、官方黄金值、属性测试、变异测试或专家验收。
因此旧页“规则已穷举测试”“政策版本黄金用例”属于愿景,不是事实。模块 Readiness Matrix 将其列 Out of Scope,也不能代替质量证明。
7. 黄金用例
每条黄金用例保存:业务场景、规范输入、预期逐步明细、法源版本/hash、专家签署、生成日期和适用区间。只断言最终金额不足以发现错档、错日期或中间舍入。
黄金用例应来自官方示例、监管办事指南或由法务/税务/HR 专家独立计算并复核。法规变化时旧用例继续验证旧 policy,新用例验证新 policy。
8. 属性与边界测试
- 非负费率下,合法分档金额增加不应使佣金/税额下降;
- 还款计划本金和等于原本金,最后剩余为0;
- Allocation 各抵扣+Remaining 等于 Payment;
- 同一时间段拆分后利息与未拆分一致(同费率/本金/日计数);
- Leave tiers 在边界前后只发生预期跳变;
- 成功金额小数位和舍入策略一致;
- 非法/溢出/超容量输入返回稳定 Error,不抛未分类异常。
属性只证明数学不变量,不证明法源选择正确,必须与黄金用例互补。
9. 差分与变异测试
用独立参考实现或高精度脚本对比 Finance/Mortgage/Bracket;故意修改一个 rate、边界、日期包含性或舍入,变异测试必须失败。若测试在政策数组被改后仍绿,说明只覆盖了 happy path。
对重要规则由第二位专家使用独立工具复算,避免测试和实现共享同一错误表。
10. 影子计算上线
新 policy 先在生产事实的脱敏副本上影子运行,不改变正式结果。比较 old/new 总额、分档、适用人群和异常比例;按金额阈值抽样人工复核。
发布应支持 tenant/cohort 灰度和即时回退到旧 policy。回退规则版本不能删除新结果记录,而应标记 Superseded 并保留审计。
11. 指标与告警
- calculation count/failure by scenario、engine version、policy version;
- invalid input/overflow/capacity rejection;
- policy missing/expired/overlap;
- old/new shadow delta bucket;
- rounding reconciliation delta;
- result ledger write/outbox failure;
- deprecated policy still used;
- official source hash changed/unreviewed。
不要用原始工资、本金、损失、产假事实作为指标 label。金额只使用有限 bucket;日志默认不写敏感值。
12. 法规变化响应
官方来源监测只能创建 Change Candidate。随后法务解释、结构化草稿、双人审批、黄金/属性/差分测试、影子运行、EffectiveFrom 发布。紧急规则也要保留审批和事后复核,不允许网页变化自动改生产计算。
政策接近 EffectiveTo 且无后继版本应告警并失败关闭正式用途;参考用途可明确降级,但响应必须带过期警告。
13. 备份与恢复
纯 DLL 无状态,但商业系统要备份 Policy bundle、source documents/hash、审批、黄金用例、结果 ledger、输入证据 hash、engine binary version 和 Outbox。只备份最终金额无法解释或重放。
恢复演练随机抽取历史结果,用当时 engine+policy 重算并逐行对比。依赖包必须可从受控 artifact registry 恢复,不能只依赖最新版本。
14. 商业 GA 阻断项
- 三个程序集当前零自动化测试;
- 没有政策版本、法源 hash、生效结束日、审批与结果明细;
- Governance contribution 可存在于计算程序集未加载的 Host;
- Auction 默认分档无可核验具体来源;
- 固定比例 5% 规则没有区分法定场景;
- Finance 税与奖金规则无 TaxYear,奖金政策存在 2027 到期;
- Penalty 130% 直接给“调整金额”,没有裁量因素;
- InterestSegment 文档包含性与代码不一致,且不验时间轴;
- HR 月份折算不是官方日历日公式,预置政策可能过期;
- Leave EffectiveFrom 取进程当天,无法跨日复现;
- Bracket、Mortgage、LeavePolicy 的输入/溢出/容量约束不足;
- 没有授权 wrapper、敏感数据治理、审计、影子发布和恢复证据。
15. 发布门禁
# 当前应显示 0 个计算器测试引用;GA 后必须有稳定命中。rg -n "AuctionCommissionCalculator|IncomeTaxCalculator|LeaveCalculator|MortgageCalculator" \ tests -g '*.cs'
# 临时硬编码和日期/比例边界的全局清单。rg -n "DefaultAuctionBrackets|ExcessivePenaltyRatio|DateTime.Today|workedMonths / 12m|EndDate.Date -" \ src/Platform/IndustryExtensions -g '*.cs'
# 目标证据链。rg -n "PolicyVersion|SourceHash|EffectiveTo|GoldenCase|ShadowCalculation" \ src tests -g '*.cs'