补齐员工代收款账单能力证据链与可达操作索引

This commit is contained in:
2026-09-11 15:20:21 +08:00
parent ff1362df3f
commit e687a266e6
3 changed files with 339 additions and 8 deletions

View File

@@ -1781,7 +1781,8 @@
"requirements": [
"agent-funds-commission::充值边界",
"identity-access::数据范围拒绝",
"order-payment-wallet::金额单位"
"order-payment-wallet::金额单位",
"employee-collection-bill::员工代收款账单来源、金额与建账判据"
],
"classification": "behavior"
},
@@ -2187,7 +2188,8 @@
"requirements": [
"identity-access::数据范围拒绝",
"order-refund-exchange::订单、退款与换货状态门禁",
"package-lifecycle::套餐使用记录价格快照"
"package-lifecycle::套餐使用记录价格快照",
"employee-collection-bill::员工代收款账单来源、金额与建账判据"
],
"classification": "behavior"
},
@@ -2783,7 +2785,8 @@
"entry": "POST /api/callback/wecom/approval/{application_id}",
"capability": "external-integration",
"requirements": [
"external-integration::企微回调与补偿"
"external-integration::企微回调与补偿",
"employee-collection-bill::核销申请审批、重提与幂等"
],
"classification": "behavior"
},
@@ -3237,14 +3240,20 @@
"entry_type": "async",
"entry": "constants.OutboxEventTypeApprovalSubmissionRequested",
"capability": "infrastructure",
"requirements": [],
"requirements": [
"employee-collection-bill::核销申请审批、重提与幂等"
],
"classification": "route_index_or_infrastructure"
},
{
"entry_type": "async",
"entry": "constants.OutboxEventTypeApprovalTerminalDecision",
"capability": "infrastructure",
"requirements": [],
"requirements": [
"employee-collection-bill::员工代收款账单来源、金额与建账判据",
"employee-collection-bill::核销申请审批、重提与幂等",
"employee-collection-bill::收款方式字典、退款联动与可追溯性"
],
"classification": "route_index_or_infrastructure"
},
{
@@ -3557,7 +3566,8 @@
"capability": "external-integration",
"requirements": [
"external-integration::企微回调与补偿",
"external-integration::外部调用重试边界"
"external-integration::外部调用重试边界",
"employee-collection-bill::核销申请审批、重提与幂等"
],
"classification": "behavior"
},
@@ -3566,7 +3576,8 @@
"entry": "constants.TaskTypeWeComApprovalSync",
"capability": "external-integration",
"requirements": [
"external-integration::企微审批状态"
"external-integration::企微审批状态",
"employee-collection-bill::核销申请审批、重提与幂等"
],
"classification": "behavior"
},
@@ -3762,5 +3773,116 @@
"merchant-payment-routing::商户池唯一性与轮询配置"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "GET /api/admin/employee-collection-payment-methods",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::收款方式字典、退款联动与可追溯性"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "POST /api/admin/employee-collection-payment-methods",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::收款方式字典、退款联动与可追溯性"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "PUT /api/admin/employee-collection-payment-methods/{id}",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::收款方式字典、退款联动与可追溯性"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "DELETE /api/admin/employee-collection-payment-methods/{id}",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::收款方式字典、退款联动与可追溯性"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "GET /api/admin/employee-collection-bills",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::账单余额、状态与关闭"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "GET /api/admin/employee-collection-bills/statistics",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::账单余额、状态与关闭"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "GET /api/admin/employee-collection-bills/{id}",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::账单余额、状态与关闭"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "POST /api/admin/employee-collection-bills/{id}/close",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::账单余额、状态与关闭"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "POST /api/admin/employee-collection-applications",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::外部付款核销申请与分摊",
"employee-collection-bill::核销申请审批、重提与幂等"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "GET /api/admin/employee-collection-applications",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::外部付款核销申请与分摊",
"employee-collection-bill::账单余额、状态与关闭"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "GET /api/admin/employee-collection-applications/{id}",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::外部付款核销申请与分摊",
"employee-collection-bill::账单余额、状态与关闭"
],
"classification": "behavior"
},
{
"entry_type": "http",
"entry": "PUT /api/admin/employee-collection-applications/{id}",
"capability": "employee-collection-bill",
"requirements": [
"employee-collection-bill::核销申请审批、重提与幂等"
],
"classification": "behavior"
}
]

View File

@@ -2669,5 +2669,198 @@
],
"exit_status": 0
}
}
},
{
"capability": "employee-collection-bill",
"requirement": "员工代收款账单来源、金额与建账判据",
"spec": "openspec/specs/employee-collection-bill/spec.md",
"entries": [
"POST /api/admin/agent-recharges/{id}/offline-pay",
"POST /api/admin/orders",
"constants.OutboxEventTypeApprovalTerminalDecision"
],
"handler_consumer_job": [
"internal/service/order/service.go:Service.CreateAdminOrder",
"internal/service/agent_recharge/service.go:Service.OfflinePay",
"internal/application/agentrecharge/approval_decision.go:ApprovalDecisionHandler.Handle"
],
"application_service_query": [
"internal/application/employeecollection/bill_creation.go:BillCreationService.CreateFromOrderInTx",
"internal/application/employeecollection/bill_creation.go:BillCreationService.CreateFromRechargeInTx"
],
"domain_state_amount": [
"internal/domain/employeecollection/order_bill.go:ShouldCreateBillForOrder",
"internal/domain/employeecollection/order_bill.go:ShouldCreateBillForRecharge",
"internal/domain/employeecollection/order_bill.go:OrderBillSubjectFromOrder"
],
"store_migration_config": [
"internal/model/employee_collection.go:EmployeeCollectionBill",
"migrations/000212_add_employee_collection_bills.up.sql:tb_employee_collection_bill"
],
"verification": {
"command": "python3 -c 'from pathlib import Path; checks=[(\"internal/domain/employeecollection/order_bill.go\", \"func ShouldCreateBillForOrder(subject OrderBillSubject) bool\"), (\"internal/domain/employeecollection/order_bill.go\", \"subject.PaymentMethod == model.PaymentMethodOffline\"), (\"internal/service/order/service.go\", \"s.billCreation.CreateFromOrderInTx(ctx, tx, order, hasGiftPackage)\"), (\"internal/service/agent_recharge/service.go\", \"s.billCreation.CreateFromRechargeInTx(ctx, tx, record)\"), (\"internal/application/agentrecharge/approval_decision.go\", \"h.billCreation.CreateFromRechargeInTx(ctx, tx, record)\"), (\"internal/application/employeecollection/bill_creation.go\", \"Columns: []clause.Column{{Name: \\\"source_key\\\"}}\")]; negative=[]; missing=[f\"{p}:{t}\" for p,t in checks if t not in Path(p).read_text()]; present=[f\"{p}:{t}\" for p,t in negative if t in Path(p).read_text()]; assert not missing and not present, {\"missing\": missing, \"present\": present}; print(\"OK: employee-collection-bill::员工代收款账单来源、金额与建账判据\")'",
"literal_output": [
"OK: employee-collection-bill::员工代收款账单来源、金额与建账判据"
],
"exit_status": 0
}
},
{
"capability": "employee-collection-bill",
"requirement": "账单余额、状态与关闭",
"spec": "openspec/specs/employee-collection-bill/spec.md",
"entries": [
"GET /api/admin/employee-collection-bills",
"GET /api/admin/employee-collection-bills/statistics",
"GET /api/admin/employee-collection-bills/{id}",
"POST /api/admin/employee-collection-bills/{id}/close",
"GET /api/admin/employee-collection-applications",
"GET /api/admin/employee-collection-applications/{id}"
],
"handler_consumer_job": [
"internal/handler/admin/employee_collection.go:EmployeeCollectionHandler.ListBills",
"internal/handler/admin/employee_collection.go:EmployeeCollectionHandler.StatisticsBills",
"internal/handler/admin/employee_collection.go:EmployeeCollectionHandler.GetBill",
"internal/handler/admin/employee_collection.go:EmployeeCollectionHandler.CloseBill"
],
"application_service_query": [
"internal/query/employeecollection/bill.go:BillQuery.List/Statistics/Detail",
"internal/query/employeecollection/bill.go:applyBillVisibility",
"internal/query/employeecollection/application.go:applyApplicationVisibility",
"internal/application/employeecollection/bill_close.go:BillCloseService.Close"
],
"domain_state_amount": [
"internal/domain/employeecollection/bill.go:BillAmounts.Reserve/Release/Approve",
"internal/domain/employeecollection/bill.go:BillAmounts.DerivedStatus",
"internal/domain/employeecollection/bill.go:ValidateBillClose"
],
"store_migration_config": [
"internal/model/employee_collection.go:EmployeeCollectionBill",
"migrations/000212_add_employee_collection_bills.up.sql:tb_employee_collection_bill"
],
"verification": {
"command": "python3 -c 'from pathlib import Path; checks=[(\"internal/domain/employeecollection/bill.go\", \"func (a BillAmounts) Reserve(amount int64) (BillAmounts, error)\"), (\"internal/domain/employeecollection/bill.go\", \"func (a BillAmounts) DerivedStatus() int\"), (\"internal/application/employeecollection/bill_close.go\", \"func (s *BillCloseService) Close(ctx context.Context, id uint, reason string)\"), (\"internal/query/employeecollection/bill.go\", \"func applyBillVisibility(ctx context.Context, query *gorm.DB) (*gorm.DB, error)\"), (\"internal/query/employeecollection/bill.go\", \"func (q *BillQuery) Statistics(\"), (\"internal/query/employeecollection/application.go\", \"func applyApplicationVisibility(ctx context.Context, query *gorm.DB) (*gorm.DB, error)\")]; negative=[]; missing=[f\"{p}:{t}\" for p,t in checks if t not in Path(p).read_text()]; present=[f\"{p}:{t}\" for p,t in negative if t in Path(p).read_text()]; assert not missing and not present, {\"missing\": missing, \"present\": present}; print(\"OK: employee-collection-bill::账单余额、状态与关闭\")'",
"literal_output": [
"OK: employee-collection-bill::账单余额、状态与关闭"
],
"exit_status": 0
}
},
{
"capability": "employee-collection-bill",
"requirement": "外部付款核销申请与分摊",
"spec": "openspec/specs/employee-collection-bill/spec.md",
"entries": [
"POST /api/admin/employee-collection-applications",
"GET /api/admin/employee-collection-applications",
"GET /api/admin/employee-collection-applications/{id}"
],
"handler_consumer_job": [
"internal/handler/admin/employee_collection.go:EmployeeCollectionHandler.CreateApplication",
"internal/handler/admin/employee_collection.go:EmployeeCollectionHandler.ListApplications",
"internal/handler/admin/employee_collection.go:EmployeeCollectionHandler.GetApplication",
"internal/handler/admin/employee_collection.go:bindApplicationRequest"
],
"application_service_query": [
"internal/application/employeecollection/application.go:ApplicationService.Create/submit",
"internal/application/employeecollection/application.go:lockBillsInAscendingOrder",
"internal/application/employeecollection/application.go:createAllocations",
"internal/query/employeecollection/application.go:ApplicationQuery.List/Detail"
],
"domain_state_amount": [
"internal/domain/employeecollection/allocation.go:ValidateAllocations",
"internal/domain/employeecollection/bill.go:BillAmounts.Reserve"
],
"store_migration_config": [
"migrations/000212_add_employee_collection_bills.up.sql:tb_employee_collection_application",
"migrations/000212_add_employee_collection_bills.up.sql:tb_employee_collection_application_allocation",
"internal/model/employee_collection.go:EmployeeCollectionApplicationAllocation"
],
"verification": {
"command": "python3 -c 'from pathlib import Path; checks=[(\"internal/domain/employeecollection/allocation.go\", \"func ValidateAllocations(paidAmount int64, candidates []AllocationCandidate) error\"), (\"internal/application/employeecollection/application.go\", \"func (s *ApplicationService) Create(ctx context.Context, command SubmitApplicationCommand) (*ApplicationSubmitResult, error)\"), (\"internal/handler/admin/employee_collection.go\", \"func (h *EmployeeCollectionHandler) CreateApplication(c *fiber.Ctx) error\")]; negative=[]; missing=[f\"{p}:{t}\" for p,t in checks if t not in Path(p).read_text()]; present=[f\"{p}:{t}\" for p,t in negative if t in Path(p).read_text()]; assert not missing and not present, {\"missing\": missing, \"present\": present}; print(\"OK: employee-collection-bill::外部付款核销申请与分摊\")'",
"literal_output": [
"OK: employee-collection-bill::外部付款核销申请与分摊"
],
"exit_status": 0
}
},
{
"capability": "employee-collection-bill",
"requirement": "核销申请审批、重提与幂等",
"spec": "openspec/specs/employee-collection-bill/spec.md",
"entries": [
"POST /api/callback/wecom/approval/{application_id}",
"constants.OutboxEventTypeApprovalSubmissionRequested",
"constants.OutboxEventTypeApprovalTerminalDecision",
"constants.TaskTypeWeComApprovalRecovery",
"constants.TaskTypeWeComApprovalSync",
"POST /api/admin/employee-collection-applications",
"PUT /api/admin/employee-collection-applications/{id}"
],
"handler_consumer_job": [
"internal/application/employeecollection/approval_decision.go:ApprovalDecisionHandler.Handle",
"internal/handler/callback/wecom_approval.go:WeComApprovalHandler.Receive",
"cmd/worker/main.go:ApprovalBusinessTypeEmployeeCollection -> employeecollectionApp.NewApprovalDecisionHandler",
"cmd/worker/main.go:TaskTypeWeComApprovalSync/TaskTypeWeComApprovalRecovery -> 既有查询与恢复机制"
],
"application_service_query": [
"internal/application/employeecollection/application.go:ApplicationService.Resubmit",
"internal/application/employeecollection/approval_decision.go:applyApproved/applyClosed/applyRevoked/revokeApproved/revokePending",
"internal/application/approval/create.go:CreationService.CreateInTx",
"internal/application/employeecollection/application.go:buildAttempt/nextAttemptNo"
],
"domain_state_amount": [
"internal/domain/employeecollection/application.go:ValidateApplicationResubmit",
"pkg/constants/approval.go:ApprovalBusinessTypeEmployeeCollection"
],
"store_migration_config": [
"migrations/000212_add_employee_collection_bills.up.sql:tb_employee_collection_application_attempt",
"internal/model/employee_collection.go:EmployeeCollectionApplicationAttempt"
],
"verification": {
"command": "python3 -c 'from pathlib import Path; checks=[(\"internal/application/employeecollection/application.go\", \"func (s *ApplicationService) Resubmit(ctx context.Context, applicationID uint, command SubmitApplicationCommand) (*ApplicationSubmitResult, error)\"), (\"internal/application/employeecollection/approval_decision.go\", \"func (h *ApprovalDecisionHandler) Handle(ctx context.Context, event approvalapp.TerminalDecisionEvent) error\"), (\"internal/application/employeecollection/approval_decision.go\", \"func (h *ApprovalDecisionHandler) revokePending(\"), (\"pkg/constants/approval.go\", \"ApprovalBusinessTypeEmployeeCollection = \\\"employee_collection_approval\\\"\"), (\"cmd/worker/main.go\", \"constants.ApprovalBusinessTypeEmployeeCollection: employeecollectionApp.NewApprovalDecisionHandler(\"), (\"internal/handler/callback/wecom_approval.go\", \"func (h *WeComApprovalHandler) Receive(c *fiber.Ctx) error\")]; negative=[]; missing=[f\"{p}:{t}\" for p,t in checks if t not in Path(p).read_text()]; present=[f\"{p}:{t}\" for p,t in negative if t in Path(p).read_text()]; assert not missing and not present, {\"missing\": missing, \"present\": present}; print(\"OK: employee-collection-bill::核销申请审批、重提与幂等\")'",
"literal_output": [
"OK: employee-collection-bill::核销申请审批、重提与幂等"
],
"exit_status": 0
}
},
{
"capability": "employee-collection-bill",
"requirement": "收款方式字典、退款联动与可追溯性",
"spec": "openspec/specs/employee-collection-bill/spec.md",
"entries": [
"constants.OutboxEventTypeApprovalTerminalDecision",
"GET /api/admin/employee-collection-payment-methods",
"POST /api/admin/employee-collection-payment-methods",
"PUT /api/admin/employee-collection-payment-methods/{id}",
"DELETE /api/admin/employee-collection-payment-methods/{id}"
],
"handler_consumer_job": [
"internal/handler/admin/employee_collection.go:EmployeeCollectionHandler.ListPaymentMethods/CreatePaymentMethod/UpdatePaymentMethod/DeletePaymentMethod",
"internal/routes/employee_collection.go:requireEmployeeCollectionConfigAccess",
"internal/service/refund/approval_decision.go:Service.applyApprovedDecision"
],
"application_service_query": [
"internal/application/employeecollection/payment_method.go:PaymentMethodService.Create/Update/Delete",
"internal/application/employeecollection/refund_offset.go:RefundOffsetService.ApplyInTx"
],
"domain_state_amount": [
"internal/domain/employeecollection/bill.go:BillAmounts.ReduceReceivable",
"internal/domain/employeecollection/refund_offset.go:DecideRefundOffset"
],
"store_migration_config": [
"migrations/000212_add_employee_collection_bills.up.sql:tb_employee_collection_payment_method",
"migrations/000212_add_employee_collection_bills.up.sql:tb_employee_collection_bill_refund",
"internal/model/employee_collection.go:EmployeeCollectionPaymentMethod",
"internal/model/employee_collection.go:EmployeeCollectionBillRefund"
],
"verification": {
"command": "python3 -c 'from pathlib import Path; checks=[(\"internal/application/employeecollection/payment_method.go\", \"func (s *PaymentMethodService) Delete(ctx context.Context, id uint) error\"), (\"internal/application/employeecollection/payment_method.go\", \"func countPaymentMethodReferences(\"), (\"internal/routes/employee_collection.go\", \"func requireEmployeeCollectionConfigAccess(handler fiber.Handler) fiber.Handler\"), (\"internal/application/employeecollection/refund_offset.go\", \"func (s *RefundOffsetService) ApplyInTx(ctx context.Context, tx *gorm.DB, source RefundOffsetSource) error\"), (\"internal/domain/employeecollection/refund_offset.go\", \"func DecideRefundOffset(bill BillAmounts, refundAmount int64) (RefundOffsetDecision, error)\"), (\"internal/domain/employeecollection/bill.go\", \"func (a BillAmounts) ReduceReceivable(amount int64) (BillAmounts, error)\"), (\"internal/service/refund/approval_decision.go\", \"s.refundOffset.ApplyInTx(ctx, tx, employeecollectionapp.RefundOffsetSource{\")]; negative=[]; missing=[f\"{p}:{t}\" for p,t in checks if t not in Path(p).read_text()]; present=[f\"{p}:{t}\" for p,t in negative if t in Path(p).read_text()]; assert not missing and not present, {\"missing\": missing, \"present\": present}; print(\"OK: employee-collection-bill::收款方式字典、退款联动与可追溯性\")'",
"literal_output": [
"OK: employee-collection-bill::收款方式字典、退款联动与可追溯性"
],
"exit_status": 0
}
}
]

View File

@@ -125,3 +125,19 @@
#### Scenario: 被引用字典项停用
- **WHEN** 超级管理员停用已被核销申请引用的线下收款方式
- **THEN** 新申请不可选择该方式,历史申请仍展示其冻结名称和稳定编码
## 可达操作索引
本节只用于入口导航,不是行为 Requirement业务义务以上述 Requirements 为准。
### 线下收款方式字典
`GET /api/admin/employee-collection-payment-methods`(查询线下收款方式);`POST /api/admin/employee-collection-payment-methods`(创建线下收款方式);`PUT /api/admin/employee-collection-payment-methods/{id}`(更新线下收款方式);`DELETE /api/admin/employee-collection-payment-methods/{id}`(删除线下收款方式)。
### 员工代收款账单
`GET /api/admin/employee-collection-bills`(查询本人或全部账单);`GET /api/admin/employee-collection-bills/statistics`(账单金额统计);`GET /api/admin/employee-collection-bills/{id}`(查询账单详情);`POST /api/admin/employee-collection-bills/{id}/close`(关闭账单)。
### 核销申请
`POST /api/admin/employee-collection-applications`(创建核销申请);`GET /api/admin/employee-collection-applications`(查询核销申请);`GET /api/admin/employee-collection-applications/{id}`(查询核销申请详情);`PUT /api/admin/employee-collection-applications/{id}`(修改并重提核销申请)。