Files
lotteryAdmin/src/lib/agent-settlement-period-range.ts
kang 24fd7c10bd feat(settlement, admin): introduce new types and functions for downline share and settlement period hints
Added new types for downline share breakdown and settlement period open hints to enhance the agent settlement API. Updated the admin console components to support these new features, improving the user experience with better data presentation and interaction. Additionally, refined the date range field to accommodate new calendar markers and hints, ensuring a more intuitive interface for managing settlement periods.
2026-06-12 16:01:42 +08:00

159 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const DATE_YMD_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
function pad2(n: number): string {
return String(n).padStart(2, "0");
}
/** 本地日历 `YYYY-MM-DD` */
export function toDateYmdValue(date: Date): string {
return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
}
function parseDateYmd(value: string): Date | null {
const match = DATE_YMD_RE.exec(value.trim());
if (!match) {
return null;
}
const [, y, mo, d] = match;
const date = new Date(Number(y), Number(mo) - 1, Number(d));
return Number.isNaN(date.getTime()) ? null : date;
}
function formatUtcDateTimeFromMs(ms: number): string {
const date = new Date(ms);
return `${date.getUTCFullYear()}-${pad2(date.getUTCMonth() + 1)}-${pad2(date.getUTCDate())} ${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}:${pad2(date.getUTCSeconds())}`;
}
/** 本地自然日 00:00:00 → UTC `YYYY-MM-DD HH:mm:ss`(开账 API */
export function localDateYmdToUtcPeriodStart(ymd: string): string {
const date = parseDateYmd(ymd);
if (date === null) {
return ymd;
}
const ms = new Date(
date.getFullYear(),
date.getMonth(),
date.getDate(),
0,
0,
0,
0,
).getTime();
return formatUtcDateTimeFromMs(ms);
}
/** 本地自然日 23:59:59 → UTC `YYYY-MM-DD HH:mm:ss`(开账 API */
export function localDateYmdToUtcPeriodEnd(ymd: string): string {
const date = parseDateYmd(ymd);
if (date === null) {
return ymd;
}
const ms = new Date(
date.getFullYear(),
date.getMonth(),
date.getDate(),
23,
59,
59,
999,
).getTime();
return formatUtcDateTimeFromMs(ms);
}
export function localDateRangeToUtcPeriodBounds(
startYmd: string,
endYmd: string,
): { period_start: string; period_end: string } {
return {
period_start: localDateYmdToUtcPeriodStart(startYmd),
period_end: localDateYmdToUtcPeriodEnd(endYmd),
};
}
/** UTC 账期时刻 → 本地日历 `YYYY-MM-DD`(列表展示) */
export function utcPeriodInstantToLocalYmd(iso: string | null | undefined): string {
if (iso == null || iso === "") {
return "—";
}
const ms = Date.parse(iso);
if (Number.isNaN(ms)) {
return iso.slice(0, 10);
}
return toDateYmdValue(new Date(ms));
}
/** UTC 存储日 `Y-m-d` → 本地日历标记用 `yyyy-MM-dd` */
export function utcStorageDateToLocalMarkYmd(utcYmd: string): string {
const match = DATE_YMD_RE.exec(utcYmd.trim());
if (!match) {
return utcYmd;
}
const [, y, mo, d] = match;
const ms = Date.UTC(Number(y), Number(mo) - 1, Number(d), 12, 0, 0, 0);
return toDateYmdValue(new Date(ms));
}
export function utcStorageDatesToLocalMarks(dates: string[]): string[] {
return dates.map((day) => utcStorageDateToLocalMarkYmd(day));
}
/** 开账建议UTC 存储日 → 本地表单 `yyyy-MM-dd` */
export function utcStorageDateToLocalFormYmd(utcYmd: string): string {
return utcStorageDateToLocalMarkYmd(utcYmd);
}
export function formatSettlementPeriodSpan(
periodStart: string | undefined,
periodEnd: string | undefined,
): string {
const start = utcPeriodInstantToLocalYmd(periodStart);
const end = utcPeriodInstantToLocalYmd(periodEnd);
return `${start} ~ ${end}`;
}
export function isSettlementLocalDateRangeValid(startYmd: string, endYmd: string): boolean {
const start = parseDateYmd(startYmd);
const end = parseDateYmd(endYmd);
if (start === null || end === null) {
return false;
}
return start.getTime() <= end.getTime();
}
function eachLocalYmdInRange(startYmd: string, endYmd: string): string[] {
const start = parseDateYmd(startYmd);
const end = parseDateYmd(endYmd);
if (start === null || end === null || start.getTime() > end.getTime()) {
return [];
}
const days: string[] = [];
const cursor = new Date(start.getFullYear(), start.getMonth(), start.getDate());
const endMs = new Date(end.getFullYear(), end.getMonth(), end.getDate()).getTime();
while (cursor.getTime() <= endMs) {
days.push(toDateYmdValue(cursor));
cursor.setDate(cursor.getDate() + 1);
}
return days;
}
/** 本地账期范围是否与已有账期日期重叠 */
export function settlementRangeOverlapsOccupiedDates(
startYmd: string,
endYmd: string,
occupiedLocalYmds: string[],
): boolean {
if (occupiedLocalYmds.length === 0) {
return false;
}
const occupied = new Set(occupiedLocalYmds);
return eachLocalYmdInRange(startYmd, endYmd).some((day) => occupied.has(day));
}