倍率问题和图片问题调整
This commit is contained in:
1 parent
f1993eb388
commit
3f29ba716a
20 files changed
+1210
-24
No files matched your search
@@ -0,0 +1,27 @@
|
||||
import type { BillingParameterDimension, BillingParameterTier, BillingPriceRule } from "@/lib/types";
|
||||
|
||||
export type PriceEditTarget = {
|
||||
dimensionKey?: string;
|
||||
tierValue?: string;
|
||||
label: string;
|
||||
markupMultiplier: number;
|
||||
};
|
||||
|
||||
export function groupedPriceRuleSections(rules: BillingPriceRule[]) {
|
||||
return rules.map((rule, index) => ({
|
||||
rule,
|
||||
label: rule.provider === "seedream" && rule.capability === "image.generate"
|
||||
? rule.conditions?.layerDecomposition === true ? "图层拆分" : "基础生图"
|
||||
: rule.variantKey || `规则 ${index + 1}`,
|
||||
dimensions: rule.parameterDimensions?.filter((dimension) => dimension.tiers.length > 0) || []
|
||||
}));
|
||||
}
|
||||
|
||||
export function priceTierEditTarget(dimension: BillingParameterDimension, tier: BillingParameterTier, businessLabel?: string): PriceEditTarget {
|
||||
return {
|
||||
dimensionKey: dimension.key,
|
||||
tierValue: String(tier.value),
|
||||
label: [businessLabel, dimension.label || dimension.key, tier.label || String(tier.value)].filter(Boolean).join(" · "),
|
||||
markupMultiplier: tier.markupMultiplier
|
||||
};
|
||||
}
|
||||
@@ -19,7 +19,7 @@ export async function downloadFilesAsZip(files: DownloadableFile[], archiveName:
|
||||
const response = await fetch(file.url, { credentials: "same-origin" });
|
||||
if (!response.ok) throw new Error(`下载第 ${index + 1} 个图层失败`);
|
||||
const data = new Uint8Array(await response.arrayBuffer());
|
||||
const safeName = uniqueFileName(sanitizeFileName(file.name) || `layer-${index}.png`, usedNames);
|
||||
const safeName = uniqueFileName(imageFileName(sanitizeFileName(file.name) || `layer-${index}`, data), usedNames);
|
||||
entries.push({ name: new TextEncoder().encode(safeName), data, crc: crc32(data), offset: 0 });
|
||||
}
|
||||
const blob = zipBlob(entries);
|
||||
@@ -93,6 +93,42 @@ function sanitizeFileName(value: string) {
|
||||
return value.trim().replace(/[\\/:*?"<>|\u0000-\u001f]/g, "-").replace(/^\.+/, "").slice(0, 120);
|
||||
}
|
||||
|
||||
function imageFileName(name: string, data: Uint8Array) {
|
||||
let extensions: string[] | undefined;
|
||||
let preferred: string | undefined;
|
||||
if (data.length >= 8 && data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4e && data[3] === 0x47 && data[4] === 0x0d && data[5] === 0x0a && data[6] === 0x1a && data[7] === 0x0a) {
|
||||
extensions = ["png"];
|
||||
preferred = "png";
|
||||
} else if (data.length >= 3 && data[0] === 0xff && data[1] === 0xd8 && data[2] === 0xff) {
|
||||
extensions = ["jpg", "jpeg", "jpe"];
|
||||
preferred = "jpg";
|
||||
} else if (data.length >= 14 && asciiAt(data, 0, "RIFF") && asciiAt(data, 8, "WEBPVP")) {
|
||||
extensions = ["webp"];
|
||||
preferred = "webp";
|
||||
} else if (data.length >= 6 && (asciiAt(data, 0, "GIF87a") || asciiAt(data, 0, "GIF89a"))) {
|
||||
extensions = ["gif"];
|
||||
preferred = "gif";
|
||||
} else if (data.length >= 2 && asciiAt(data, 0, "BM")) {
|
||||
extensions = ["bmp"];
|
||||
preferred = "bmp";
|
||||
} else if (data.length >= 4 && data[0] === 0 && data[1] === 0 && (data[2] === 1 || data[2] === 2) && data[3] === 0) {
|
||||
extensions = ["ico"];
|
||||
preferred = "ico";
|
||||
}
|
||||
if (!preferred || !extensions) return name;
|
||||
const dot = name.lastIndexOf(".");
|
||||
const current = dot > 0 ? name.slice(dot + 1) : "";
|
||||
if (extensions.includes(current.toLowerCase())) return name;
|
||||
return `${dot > 0 ? name.slice(0, dot) : name}.${preferred}`;
|
||||
}
|
||||
|
||||
function asciiAt(data: Uint8Array, offset: number, value: string) {
|
||||
for (let index = 0; index < value.length; index += 1) {
|
||||
if (data[offset + index] !== value.charCodeAt(index)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function uniqueFileName(name: string, used: Set<string>) {
|
||||
if (!used.has(name)) {
|
||||
used.add(name);
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
updateBillingPriceRule,
|
||||
type BillingPriceRuleInput
|
||||
} from "@/lib/server/billing-store";
|
||||
import type { BillingPriceRule } from "@/lib/types";
|
||||
import type { BillingParameterDimension, BillingPriceRule } from "@/lib/types";
|
||||
|
||||
export const BILLING_USD_CNY_RATE = 7.2;
|
||||
export const DEFAULT_BILLING_MARKUP_MULTIPLIER = 1.2;
|
||||
@@ -390,7 +390,7 @@ export async function ensureDefaultBillingPriceRules(): Promise<BillingPriceRule
|
||||
priority: candidate.priority,
|
||||
note: candidate.note,
|
||||
source: candidate.source,
|
||||
parameterDimensions: candidate.parameterDimensions
|
||||
parameterDimensions: mergeParameterDimensions(candidate.parameterDimensions, existingRule.parameterDimensions)
|
||||
});
|
||||
if (updated) {
|
||||
const index = existing.findIndex((rule) => rule.id === updated.id);
|
||||
@@ -412,6 +412,31 @@ export async function ensureDefaultBillingPriceRules(): Promise<BillingPriceRule
|
||||
return [...existing, ...created];
|
||||
}
|
||||
|
||||
function mergeParameterDimensions(
|
||||
defaults: BillingParameterDimension[] | undefined,
|
||||
saved: BillingParameterDimension[] | undefined
|
||||
): BillingParameterDimension[] | undefined {
|
||||
return defaults?.map((dimension) => {
|
||||
const savedDimension = saved?.find((item) => item.key === dimension.key);
|
||||
return {
|
||||
...dimension,
|
||||
tiers: dimension.tiers.map((tier) => {
|
||||
const savedTier = savedDimension?.tiers.find((item) => String(item.value) === String(tier.value));
|
||||
const savedMarkup = savedTier?.markupMultiplier;
|
||||
return {
|
||||
...tier,
|
||||
markupMultiplier: typeof savedMarkup === "number"
|
||||
&& Number.isFinite(savedMarkup)
|
||||
&& savedMarkup >= 1
|
||||
&& savedMarkup <= 1000
|
||||
? savedMarkup
|
||||
: tier.markupMultiplier
|
||||
};
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function priceRuleMatchKey(rule: Pick<BillingPriceRule, "provider" | "capability" | "reqKey" | "variantKey" | "conditions">): string {
|
||||
const conditions = Object.fromEntries(Object.entries(rule.conditions || {}).sort(([left], [right]) => left.localeCompare(right)));
|
||||
return [rule.provider, rule.capability, rule.reqKey || "", rule.variantKey || "", JSON.stringify(conditions)].join("\u0000");
|
||||
|
||||
Reference in new issue
Block a user