You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123 lines
3.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/** 登录态:首价后补充信息确认窗口(对客户不暴露时限/渠道细节) */
/**
* 决策窗:与总窗口对齐,前端不再单独倒计时逼迫用户。
* 驻留页仍需 Redis/浏览器 TTL;过短会导致填写中会话被清。
*/
export const MS_REFINE_DECISION_MS = 3_600_000;
/** 自首次驻留起的会话保留窗口(1 小时;仅内部资源回收,不对客户提示) */
export const MS_REFINE_TOTAL_MS = 3_600_000;
export const MS_REFINE_HOLD_REDIS_PREFIX = "ms_refine_hold:";
export type MsRefineHoldState = {
quote_id: string;
quote_session_id: string;
customer_id: string;
parked_at_ms: number;
decision_deadline_ms: number;
total_deadline_ms: number;
/** awaiting_decision | filling | refining | declined | refined | expired */
status:
| "awaiting_decision"
| "filling"
| "refining"
| "declined"
| "refined"
| "expired";
};
export type MsRefineHoldPublic = {
available: boolean;
quote_session_id: string;
decision_deadline_ms: number;
total_deadline_ms: number;
decision_remaining_ms: number;
total_remaining_ms: number;
};
export function buildMsRefineHoldState(input: {
quoteId: string;
sessionId: string;
customerId: string;
nowMs?: number;
}): MsRefineHoldState {
const now = input.nowMs ?? Date.now();
return {
quote_id: input.quoteId,
quote_session_id: input.sessionId,
customer_id: input.customerId,
parked_at_ms: now,
decision_deadline_ms: now + MS_REFINE_DECISION_MS,
total_deadline_ms: now + MS_REFINE_TOTAL_MS,
status: "awaiting_decision",
};
}
export function toMsRefineHoldPublic(
state: MsRefineHoldState,
nowMs = Date.now(),
): MsRefineHoldPublic {
const decision_remaining_ms = Math.max(0, state.decision_deadline_ms - nowMs);
const total_remaining_ms = Math.max(0, state.total_deadline_ms - nowMs);
/** filling/refining 期间始终可用,避免填写过程中因倒计时丢失提交入口 */
const available =
state.status === "refining" ||
state.status === "filling" ||
(state.status === "awaiting_decision" && total_remaining_ms > 0);
return {
available,
quote_session_id: state.quote_session_id,
decision_deadline_ms: state.decision_deadline_ms,
total_deadline_ms: state.total_deadline_ms,
decision_remaining_ms,
total_remaining_ms,
};
}
/**
* 不再因决策窗自动放弃:用户可随时选择继续或放弃。
* (保留函数签名,兼容 GET/服务层调用)
*/
export function shouldAutoDeclineRefineHold(
_state: MsRefineHoldState,
_nowMs = Date.now(),
): boolean {
return false;
}
/** 内部会话窗口是否已过(资源回收 / 降级重查用,不对客户直出) */
export function isMsRefineHoldExpired(
state: MsRefineHoldState,
nowMs = Date.now(),
): boolean {
return nowMs >= state.total_deadline_ms;
}
/** 倒计时文案(内部/兼容;客户 UI 不再展示) */
export function formatMsRefineRemainingLabel(
deadlineMs: number,
nowMs = Date.now(),
): string {
const sec = Math.max(0, Math.ceil((deadlineMs - nowMs) / 1000));
const m = Math.floor(sec / 60);
const s = sec % 60;
return m > 0 ? `${m}分${String(s).padStart(2, "0")}秒` : `${s}秒`;
}
/** 客户可见提示(禁止:超时 / 二级 / 官网 / Save&update 等敏感词) */
export const MS_REFINE_MSG = {
decisionTimeout: "请补充必要信息后重新获取报价",
totalTimeout: "请补充必要信息后重新获取报价",
totalTimeoutApi: "暂时无法获取报价,请稍后重试",
decisionTimeoutApi: "暂时无法获取报价,请稍后重试",
sessionGone: "报价会话已失效,请重新询价",
} as const;
/** 一级无价、需补详情后刷价(落库 error_code,status=done) */
export const MS_NEEDS_DETAILS_ERROR_CODE = "MS_NEEDS_DETAILS" as const;
export const MS_NEEDS_DETAILS_MESSAGE =
"请补充必要信息后重新获取报价";