补齐审批退回结果通知 [09-04-approval-return-notification-gap]
这个提交包含在:
@@ -550,6 +550,68 @@ reimbursement UI renders only declared capabilities
|
||||
legacy reject endpoint and rejected history remain compatible but undiscoverable from the new UI
|
||||
```
|
||||
|
||||
## Scenario: Return Outcome Notifications
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- Trigger: adding a workflow entity that supports return, or changing return outcome notifications, recipients, copy, deep links or IM routing.
|
||||
- These are runtime notification projections of a completed return, not new public business-domain events or another workflow state transition.
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- Runtime types: `reimbursement.returned`, `leave.returned`, `purchase_request.returned` and `travel_request.returned`.
|
||||
- Domain scopes: reimbursement -> `reimbursement`, leave -> `leave`, purchase request -> `procurement`, travel request -> `travel`.
|
||||
- Attendance correction has no return action and therefore no returned notification type.
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- Every returned type must be registered together in `NOTIFICATION_TYPES`, `IM_SUPPORTED_NOTIFICATION_TYPES` and `NOTIFICATION_TYPE_TO_DOMAIN_SCOPE`. A type missing from any one registry is incomplete even if station notification creation still works.
|
||||
- The workflow outcome mapping is the only entity-to-returned-type switch. Station notifications, email tasks and IM dispatch tasks consume that same projection; do not add channel-local entity branches.
|
||||
- Return copy says the request was returned and can be modified and resubmitted. It must not reuse rejected wording while the business object is restored to `draft`.
|
||||
- Reuse each entity's existing applicant/submitter resolution and deep link. Duplicate employee IDs produce one station notification and one recipient entry.
|
||||
- Business state, workflow task/history and leave/budget release remain authoritative. Notification construction or dispatch failure is logged without sensitive provider payloads and never rolls back a completed return.
|
||||
- Production channel routing is external configuration. Shipping support for a new type does not authorize adding it to a production channel allowlist; that write requires a separate approval and audited management-API update.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Missing explicit `allowedTypes` value or domain scope in a configured whitelist -> channel does not match; empty arrays retain the existing meaning of no restriction for that dimension.
|
||||
- Station notification creation throws after the return state/history writes -> return still succeeds and commits; no email or IM work is fabricated from the failed projection.
|
||||
- IM provider or Outbox dispatch fails after commit -> business return remains committed and delivery/outbox evidence follows the existing dispatcher contract.
|
||||
- No natural production return sample -> code and routing readiness may pass, but real enterprise-WeChat delivery stays blocked rather than being inferred from synthetic tests.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: a returned purchase request creates one `purchase_request.returned` station notification and projects the same copy and target into email/IM work; the applicant sees that the draft can be modified and resubmitted.
|
||||
- Base: production routing does not yet allow the new type, so the station notification exists while IM records a deterministic routing skip; the completed return remains unchanged.
|
||||
- Bad: register a returned type only in `NOTIFICATION_TYPES`, write a channel-specific entity switch, reuse rejected copy, or let a provider failure change the workflow response.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- Table-driven registry test covering all three registries for the four supported returned types and proving the IM-supported registry has no duplicates.
|
||||
- Outcome matrix for reimbursement, leave, purchase, travel and attendance correction.
|
||||
- Side-effect tests for station notification, email task, IM dispatch task, recipient deduplication, copy, target identity and deep link for leave/purchase/travel.
|
||||
- Route-level failure test proving notification creation failure does not undo task completion, business `draft` state or return history.
|
||||
- IM routing allow/reject coverage and management API schema coverage for all newly supported values.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
#### Wrong
|
||||
|
||||
```js
|
||||
const NOTIFICATION_TYPES = Object.freeze({ leaveReturned: "leave.returned" });
|
||||
// Missing IM support/domain registration; station works but IM silently skips it.
|
||||
```
|
||||
|
||||
#### Correct
|
||||
|
||||
```js
|
||||
const NOTIFICATION_TYPES = Object.freeze({ leaveReturned: "leave.returned" });
|
||||
const IM_SUPPORTED_NOTIFICATION_TYPES = Object.freeze([NOTIFICATION_TYPES.leaveReturned]);
|
||||
const NOTIFICATION_TYPE_TO_DOMAIN_SCOPE = Object.freeze({
|
||||
[NOTIFICATION_TYPES.leaveReturned]: NOTIFICATION_DOMAIN_SCOPES.leave,
|
||||
});
|
||||
```
|
||||
|
||||
## Scenario: Shared Payment Processing Projection
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
在新工单中引用
屏蔽一个用户