feat(sign): 补齐医护端与患者端签署流程

This commit is contained in:
xy
2026-09-28 19:57:57 +08:00
parent a25716c861
commit 8024075439
22 changed files with 1082 additions and 163 deletions
+20 -27
View File
@@ -5,16 +5,20 @@ import type {
ApiResponse,
SignatureUploadInput,
SignatureUploadResponse,
SigningProof,
SigningContextResponse,
TokenConsumeRequest,
TokenConsumeResponse,
} from './types'
export function consumeSigningToken(payload: TokenConsumeRequest): Promise<TokenConsumeResponse> {
export function consumeSigningToken(
payload: TokenConsumeRequest,
idempotencyKey: string,
): Promise<TokenConsumeResponse> {
return request
.post<ApiResponse<TokenConsumeResponse>>('/v1/sign-deliveries/token/consume', payload, {
headers: {
'Idempotency-Key': createIdempotencyKey(),
'Idempotency-Key': idempotencyKey,
},
})
.then(unwrapApiResponse)
@@ -23,48 +27,37 @@ export function consumeSigningToken(payload: TokenConsumeRequest): Promise<Token
/**
* 读取本次签署的完整上下文:患者信息 / 签署人 / 文书正文 / 医生已填结果。
*
* 契约见 `.workbuddy-ai/患者端H5真实接入方案-2026-09-28.md` 第三节。
* **后端就绪前这个调用会失败**,所以调用方必须能优雅降级 ——
* 拿不到正文就不能放行签署(见 `useSignFlow` 的 `documentError`)。
* Context 使用已消费凭证,不再把原始一次性 Token 放进请求 URL、日志或后续请求。
*/
export function fetchSigningContext(token: string): Promise<SigningContextResponse> {
export function fetchSigningContext(proof: SigningProof): Promise<SigningContextResponse> {
return request
.get<ApiResponse<SigningContextResponse>>('/v1/sign-deliveries/token/context', {
params: { token },
})
.post<ApiResponse<SigningContextResponse>>('/v1/sign-deliveries/token/context', proof)
.then(unwrapApiResponse)
}
function createIdempotencyKey(value?: string) {
if (value) {
return value
}
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
return `patient-h5-${crypto.randomUUID()}`
}
return `patient-h5-${Date.now()}-${Math.random().toString(36).slice(2)}`
}
export function uploadSigningSignature(
payload: SignatureUploadInput,
): Promise<SignatureUploadResponse> {
const formData = new FormData()
formData.append('file', payload.file, 'signature.png')
formData.append('metadata', payload.metadata)
formData.append('deliveryId', payload.deliveryId)
formData.append('uploadToken', payload.uploadToken)
formData.append(
'metadata',
new Blob([JSON.stringify(payload.metadata)], { type: 'application/json' }),
)
formData.append(
'signer',
new Blob([JSON.stringify(payload.signer)], { type: 'application/json' }),
)
return request
.post<ApiResponse<SignatureUploadResponse>>(
`/v1/sign-deliveries/${encodeURIComponent(payload.taskId)}/signature`,
formData,
{
params: {
deliveryId: payload.deliveryId,
uploadToken: payload.uploadToken,
},
headers: {
'Idempotency-Key': createIdempotencyKey(payload.idempotencyKey),
'Idempotency-Key': payload.idempotencyKey,
},
},
)