add package
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import type { EventBizType, EventItem } from "@/types/EventItem";
|
||||
|
||||
/** 返回事件业务类型的前端文案。 */
|
||||
export function getEventBizTypeText(bizType?: EventBizType): string {
|
||||
return bizType === "PACKAGE_PURCHASE" ? "套餐购买" : "普通登记";
|
||||
}
|
||||
|
||||
/** 判断事件是否为套餐购买事件。 */
|
||||
export function isPackagePurchaseEvent(event?: Pick<EventItem, "bizType">): boolean {
|
||||
return event?.bizType === "PACKAGE_PURCHASE";
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import type {
|
||||
CustomerPackageItem,
|
||||
CustomerPackageStatus,
|
||||
DeliveryOperationType,
|
||||
DeliveryStatus,
|
||||
FundOperationType,
|
||||
FundRecordStatus,
|
||||
PackageConfigStatus,
|
||||
PaymentStatus,
|
||||
RefundStatus
|
||||
} from "@/types/Package";
|
||||
|
||||
/** 生成套餐业务幂等请求号。每次新操作必须重新生成。 */
|
||||
export function createPackageRequestNo(): string {
|
||||
return window.crypto.randomUUID();
|
||||
}
|
||||
|
||||
/** 将分转换为金额展示文本,不用浮点数做金额格式化。 */
|
||||
export function formatPackageAmount(cents?: number | null): string {
|
||||
if (cents === undefined || cents === null) {
|
||||
return "未设置";
|
||||
}
|
||||
const negative = cents < 0;
|
||||
const absolute = Math.abs(Math.trunc(cents));
|
||||
const yuan = Math.floor(absolute / 100);
|
||||
const fen = String(absolute % 100).padStart(2, "0");
|
||||
return `${negative ? "-" : ""}${yuan}.${fen} 元`;
|
||||
}
|
||||
|
||||
/** 将分转换为金额输入框文本。 */
|
||||
export function formatPackageAmountInput(cents?: number | null): string {
|
||||
if (cents === undefined || cents === null) {
|
||||
return "";
|
||||
}
|
||||
const negative = cents < 0;
|
||||
const absolute = Math.abs(Math.trunc(cents));
|
||||
return `${negative ? "-" : ""}${Math.floor(absolute / 100)}.${String(absolute % 100).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
/** 将用户输入的元金额精确转换为后端要求的分。 */
|
||||
export function parsePackageAmount(value: string): number | undefined {
|
||||
const text = value.trim();
|
||||
if (!/^\d+(?:\.\d{1,2})?$/.test(text)) {
|
||||
return undefined;
|
||||
}
|
||||
const [yuanText, fenText = ""] = text.split(".");
|
||||
const cents = BigInt(yuanText) * 100n + BigInt(fenText.padEnd(2, "0") || "0");
|
||||
const result = Number(cents);
|
||||
return Number.isSafeInteger(result) ? result : undefined;
|
||||
}
|
||||
|
||||
/** 规范化最多三位小数的非负定点数量。 */
|
||||
export function normalizePackageQuantity(value: number | string): string | undefined {
|
||||
const text = String(value).trim();
|
||||
if (!/^\d+(?:\.\d{1,3})?$/.test(text)) {
|
||||
return undefined;
|
||||
}
|
||||
const [integerText, fractionText = ""] = text.split(".");
|
||||
const integer = integerText.replace(/^0+(?=\d)/, "") || "0";
|
||||
const fraction = fractionText.replace(/0+$/, "");
|
||||
return fraction ? `${integer}.${fraction}` : integer;
|
||||
}
|
||||
|
||||
/** 使用 BigInt 比较定点数量,避免 JavaScript 二进制浮点误差。 */
|
||||
export function comparePackageQuantity(left: number | string, right: number | string): number {
|
||||
const leftParts = toQuantityParts(left);
|
||||
const rightParts = toQuantityParts(right);
|
||||
if (!leftParts || !rightParts) {
|
||||
return 0;
|
||||
}
|
||||
const scale = Math.max(leftParts.scale, rightParts.scale);
|
||||
const leftValue = leftParts.value * 10n ** BigInt(scale - leftParts.scale);
|
||||
const rightValue = rightParts.value * 10n ** BigInt(scale - rightParts.scale);
|
||||
return leftValue < rightValue ? -1 : rightValue < leftValue ? 1 : 0;
|
||||
}
|
||||
|
||||
export function getPackageQuantityText(value?: number | string | null): string {
|
||||
if (value === undefined || value === null || value === "") {
|
||||
return "0";
|
||||
}
|
||||
return normalizePackageQuantity(value) || "0";
|
||||
}
|
||||
|
||||
export function getPackageConfigStatusText(status?: PackageConfigStatus): string {
|
||||
return ({ DRAFT: "草稿", ACTIVE: "启用", INACTIVE: "停用" } as Record<string, string>)[status || ""] || "未知";
|
||||
}
|
||||
|
||||
export function getCustomerPackageStatusText(status?: CustomerPackageStatus): string {
|
||||
return ({ ACTIVE: "进行中", FULFILLED: "已履约", CANCELLED: "已取消" } as Record<string, string>)[status || ""] || "未知";
|
||||
}
|
||||
|
||||
export function getPaymentStatusText(status?: PaymentStatus): string {
|
||||
return ({ UNPAID: "未收款", PARTIALLY_PAID: "部分收款", PAID: "已收款" } as Record<string, string>)[status || ""] || "未知";
|
||||
}
|
||||
|
||||
export function getRefundStatusText(status?: RefundStatus): string {
|
||||
return ({ NOT_REQUIRED: "无需退款", PENDING: "待退款", REFUNDED: "已退款" } as Record<string, string>)[status || ""] || "未知";
|
||||
}
|
||||
|
||||
export function getDeliveryStatusText(status?: DeliveryStatus): string {
|
||||
return ({ PENDING: "待核销", VERIFIED: "已核销", REJECTED: "已拒绝", CANCELLED: "已撤销", REVOKED: "已冲正" } as Record<string, string>)[status || ""] || "未知";
|
||||
}
|
||||
|
||||
export function getDeliveryOperationText(type?: DeliveryOperationType): string {
|
||||
return type === "REVOKE" ? "发放冲正" : "正常发放";
|
||||
}
|
||||
|
||||
export function getFundOperationText(type?: FundOperationType): string {
|
||||
return ({ RECEIVE: "线下收款", REFUND: "退款", CONFIRM_NO_REFUND: "确认无需退款", REVOKE: "资金冲正" } as Record<string, string>)[type || ""] || "未知";
|
||||
}
|
||||
|
||||
export function getFundStatusText(status?: FundRecordStatus): string {
|
||||
return ({ PENDING: "待核销", VERIFIED: "已核销", REJECTED: "已拒绝", CANCELLED: "已撤销" } as Record<string, string>)[status || ""] || "未知";
|
||||
}
|
||||
|
||||
export function getPackageItemAvailableQuantity(item: CustomerPackageItem): string {
|
||||
return getPackageQuantityText(item.availableQuantity);
|
||||
}
|
||||
|
||||
function toQuantityParts(value: number | string): { value: bigint; scale: number } | undefined {
|
||||
const normalized = normalizePackageQuantity(value);
|
||||
if (!normalized) {
|
||||
return undefined;
|
||||
}
|
||||
const [integerText, fractionText = ""] = normalized.split(".");
|
||||
return {
|
||||
value: BigInt(`${integerText}${fractionText}`),
|
||||
scale: fractionText.length
|
||||
};
|
||||
}
|
||||
@@ -45,6 +45,35 @@ export const permissionCodes = {
|
||||
update: "GAO:POINT_RULE:UPDATE",
|
||||
delete: "GAO:POINT_RULE:DELETE"
|
||||
},
|
||||
packageConfig: {
|
||||
create: "GAO:PACKAGE_CONFIG:CREATE",
|
||||
read: "GAO:PACKAGE_CONFIG:READ",
|
||||
update: "GAO:PACKAGE_CONFIG:UPDATE",
|
||||
delete: "GAO:PACKAGE_CONFIG:DELETE"
|
||||
},
|
||||
customerPackage: {
|
||||
create: "GAO:CUSTOMER_PACKAGE:CREATE",
|
||||
read: "GAO:CUSTOMER_PACKAGE:READ",
|
||||
cancel: "GAO:CUSTOMER_PACKAGE:CANCEL",
|
||||
priceOverride: "GAO:CUSTOMER_PACKAGE:PRICE_OVERRIDE"
|
||||
},
|
||||
packageDelivery: {
|
||||
create: "GAO:PACKAGE_DELIVERY:CREATE",
|
||||
read: "GAO:PACKAGE_DELIVERY:READ",
|
||||
verify: "GAO:PACKAGE_DELIVERY:VERIFY",
|
||||
revoke: "GAO:PACKAGE_DELIVERY:REVOKE"
|
||||
},
|
||||
fundReceive: {
|
||||
create: "GAO:FUND_RECEIVE:CREATE",
|
||||
verify: "GAO:FUND_RECEIVE:VERIFY"
|
||||
},
|
||||
fundRecord: {
|
||||
read: "GAO:FUND_RECORD:READ"
|
||||
},
|
||||
fund: {
|
||||
refund: "GAO:FUND:REFUND",
|
||||
revoke: "GAO:FUND:REVOKE"
|
||||
},
|
||||
stat: {
|
||||
read: "GAO:STAT:READ"
|
||||
},
|
||||
@@ -140,6 +169,23 @@ export const gaoStoreScopePermissionCodes = [
|
||||
permissionCodes.pointRule.read,
|
||||
permissionCodes.pointRule.update,
|
||||
permissionCodes.pointRule.delete,
|
||||
permissionCodes.packageConfig.create,
|
||||
permissionCodes.packageConfig.read,
|
||||
permissionCodes.packageConfig.update,
|
||||
permissionCodes.packageConfig.delete,
|
||||
permissionCodes.customerPackage.create,
|
||||
permissionCodes.customerPackage.read,
|
||||
permissionCodes.customerPackage.cancel,
|
||||
permissionCodes.customerPackage.priceOverride,
|
||||
permissionCodes.packageDelivery.create,
|
||||
permissionCodes.packageDelivery.read,
|
||||
permissionCodes.packageDelivery.verify,
|
||||
permissionCodes.packageDelivery.revoke,
|
||||
permissionCodes.fundReceive.create,
|
||||
permissionCodes.fundReceive.verify,
|
||||
permissionCodes.fundRecord.read,
|
||||
permissionCodes.fund.refund,
|
||||
permissionCodes.fund.revoke,
|
||||
permissionCodes.stat.read,
|
||||
permissionCodes.user.create,
|
||||
permissionCodes.user.read,
|
||||
|
||||
Reference in New Issue
Block a user