192 lines
9.2 KiB
TypeScript
192 lines
9.2 KiB
TypeScript
import { z } from "zod";
|
|
import { validatePublicAppJumpParam } from "@/shared/app-jump/appJumpConfig.js";
|
|
|
|
const appBannerDisplayScopes = ["home", "room", "recharge", "me"] as const;
|
|
|
|
const requiredH5LinkURLSchema = z
|
|
.string()
|
|
.trim()
|
|
.min(1, "请填写 H5 链接")
|
|
.max(2048, "H5 链接不能超过 2048 个字符")
|
|
.refine((value) => !/\s/.test(value), "H5 链接不能包含空白字符");
|
|
|
|
const h5LinkKeySchema = z
|
|
.string()
|
|
.trim()
|
|
.min(1, "请填写 key")
|
|
.max(80, "key 不能超过 80 个字符")
|
|
.regex(/^[A-Za-z0-9_.:-]+$/, "key 只能包含字母、数字、下划线、中横线、点和冒号");
|
|
|
|
export const h5LinkUpdateSchema = z.object({
|
|
key: h5LinkKeySchema,
|
|
label: z.string().trim().min(1, "请填写配置项").max(80, "配置项不能超过 80 个字符"),
|
|
url: requiredH5LinkURLSchema,
|
|
});
|
|
|
|
export type H5LinkUpdateForm = z.infer<typeof h5LinkUpdateSchema>;
|
|
|
|
export const exploreTabSchema = z.object({
|
|
enabled: z.boolean(),
|
|
h5Url: requiredH5LinkURLSchema,
|
|
sortOrder: z.coerce.number().int("排序必须是整数").default(0),
|
|
tab: z.string().trim().min(1, "请填写 tab").max(40, "tab 不能超过 40 个字符"),
|
|
});
|
|
|
|
export type ExploreTabForm = z.infer<typeof exploreTabSchema>;
|
|
|
|
export const appBannerSchema = z
|
|
.object({
|
|
bannerType: z.enum(["h5", "app"]),
|
|
countryCode: z
|
|
.string()
|
|
.trim()
|
|
.transform((value) => value.toUpperCase())
|
|
.refine((value) => value === "" || /^[A-Z]{2,3}$/.test(value), "国家编码需为 2-3 位字母"),
|
|
coverUrl: z
|
|
.string()
|
|
.trim()
|
|
.min(1, "请上传封面图")
|
|
.max(1024, "封面图地址不能超过 1024 个字符")
|
|
.refine((value) => !/\s/.test(value), "封面图地址不能包含空白字符"),
|
|
description: z.string().trim().max(255, "描述不能超过 255 个字符"),
|
|
displayScope: z.enum(appBannerDisplayScopes).optional(),
|
|
displayScopes: z.array(z.enum(appBannerDisplayScopes)).min(1, "请选择显示范围"),
|
|
endsAtMs: z.coerce.number().int().min(0, "投放结束时间不正确").default(0),
|
|
param: z.string().trim().max(2048, "参数不能超过 2048 个字符"),
|
|
platform: z.enum(["android", "ios"]),
|
|
regionId: z.coerce.number().int().min(0, "区域不正确").default(0),
|
|
roomSmallImageUrl: z
|
|
.string()
|
|
.trim()
|
|
.max(1024, "房间内小屏图地址不能超过 1024 个字符")
|
|
.refine((value) => !/\s/.test(value), "房间内小屏图地址不能包含空白字符"),
|
|
sortOrder: z.coerce.number().int("排序必须是整数").default(0),
|
|
startsAtMs: z.coerce.number().int().min(0, "投放开始时间不正确").default(0),
|
|
status: z.enum(["active", "disabled", "expired"]),
|
|
})
|
|
.superRefine((value, ctx) => {
|
|
if (value.bannerType === "h5" && !value.param) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "H5 类型需要填写链接", path: ["param"] });
|
|
}
|
|
if (value.bannerType === "h5" && /\s/.test(value.param)) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "H5 链接不能包含空白字符", path: ["param"] });
|
|
}
|
|
if (value.bannerType === "app") {
|
|
const validation = validatePublicAppJumpParam(value.param);
|
|
if (!validation.valid) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: validation.message, path: ["param"] });
|
|
}
|
|
}
|
|
if (value.displayScopes.includes("room") && !value.roomSmallImageUrl) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: "房间内显示需要上传房间内小屏图",
|
|
path: ["roomSmallImageUrl"],
|
|
});
|
|
}
|
|
if (value.startsAtMs > 0 && value.endsAtMs > 0 && value.startsAtMs >= value.endsAtMs) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "投放结束时间必须晚于开始时间", path: ["endsAtMs"] });
|
|
}
|
|
});
|
|
|
|
export type AppBannerForm = z.infer<typeof appBannerSchema>;
|
|
|
|
export const appSplashScreenSchema = z
|
|
.object({
|
|
countryCode: z
|
|
.string()
|
|
.trim()
|
|
.transform((value) => value.toUpperCase())
|
|
.refine((value) => value === "" || /^[A-Z]{2,3}$/.test(value), "国家编码需为 2-3 位字母"),
|
|
coverUrl: z
|
|
.string()
|
|
.trim()
|
|
.min(1, "请上传封面图")
|
|
.max(1024, "封面图地址不能超过 1024 个字符")
|
|
.refine((value) => !/\s/.test(value), "封面图地址不能包含空白字符"),
|
|
description: z.string().trim().max(255, "描述不能超过 255 个字符"),
|
|
displayDurationMs: z.coerce.number().int("展示时长必须是整数毫秒").min(0, "展示时长不能小于 0").default(3000),
|
|
endsAtMs: z.coerce.number().int().min(0, "投放结束时间不正确").default(0),
|
|
param: z.string().trim().max(2048, "参数不能超过 2048 个字符"),
|
|
platform: z.enum(["android", "ios"]),
|
|
regionId: z.coerce.number().int().min(0, "区域不正确").default(0),
|
|
sortOrder: z.coerce.number().int("排序必须是整数").default(0),
|
|
splashType: z.enum(["h5", "app"]),
|
|
startsAtMs: z.coerce.number().int().min(0, "投放开始时间不正确").default(0),
|
|
status: z.enum(["active", "disabled", "expired"]),
|
|
})
|
|
.superRefine((value, ctx) => {
|
|
if (value.splashType === "h5" && !value.param) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "H5 类型需要填写链接", path: ["param"] });
|
|
}
|
|
if (value.splashType === "h5" && /\s/.test(value.param)) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "H5 链接不能包含空白字符", path: ["param"] });
|
|
}
|
|
if (value.splashType === "app") {
|
|
const validation = validatePublicAppJumpParam(value.param);
|
|
if (!validation.valid) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: validation.message, path: ["param"] });
|
|
}
|
|
}
|
|
if (value.startsAtMs > 0 && value.endsAtMs > 0 && value.startsAtMs >= value.endsAtMs) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "投放结束时间必须晚于开始时间", path: ["endsAtMs"] });
|
|
}
|
|
});
|
|
|
|
export type AppSplashScreenForm = z.infer<typeof appSplashScreenSchema>;
|
|
|
|
export const appPopupSchema = z
|
|
.object({
|
|
code: z
|
|
.string()
|
|
.trim()
|
|
.min(1, "请填写弹窗code")
|
|
.max(80, "弹窗code不能超过 80 个字符")
|
|
.regex(/^[A-Za-z0-9_.:-]+$/, "弹窗code只能包含字母、数字、下划线、中横线、点和冒号"),
|
|
displayPeriodDays: z.coerce.number().int("展示周期必须是整数天").min(0, "展示周期不能小于 0").default(0),
|
|
endsAtMs: z.coerce.number().int().min(0, "生效结束时间不正确").default(0),
|
|
imageUrl: z
|
|
.string()
|
|
.trim()
|
|
.max(1024, "图片地址不能超过 1024 个字符")
|
|
.refine((value) => !/\s/.test(value), "图片地址不能包含空白字符"),
|
|
jumpType: z.enum(["h5", "app"]),
|
|
jumpUrl: z.string().trim().min(1, "请填写跳转地址").max(2048, "跳转地址不能超过 2048 个字符"),
|
|
name: z.string().trim().min(1, "请填写弹窗名字").max(80, "弹窗名字不能超过 80 个字符"),
|
|
sortOrder: z.coerce.number().int("排序必须是整数").default(0),
|
|
startsAtMs: z.coerce.number().int().min(0, "生效开始时间不正确").default(0),
|
|
status: z.enum(["active", "disabled", "expired"]),
|
|
})
|
|
.superRefine((value, ctx) => {
|
|
if (value.jumpType === "h5" && /\s/.test(value.jumpUrl)) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "H5 链接不能包含空白字符", path: ["jumpUrl"] });
|
|
}
|
|
if (value.jumpType === "app") {
|
|
const validation = validatePublicAppJumpParam(value.jumpUrl);
|
|
if (!validation.valid) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: validation.message, path: ["jumpUrl"] });
|
|
}
|
|
}
|
|
if (value.startsAtMs > 0 && value.endsAtMs > 0 && value.startsAtMs >= value.endsAtMs) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "生效结束时间必须晚于开始时间", path: ["endsAtMs"] });
|
|
}
|
|
});
|
|
|
|
export type AppPopupForm = z.infer<typeof appPopupSchema>;
|
|
|
|
export const appVersionSchema = z.object({
|
|
buildNumber: z.coerce.number().int("编译版本号必须是整数").positive("编译版本号必须大于 0"),
|
|
description: z.string().trim().max(1000, "更新描述不能超过 1000 个字符").default(""),
|
|
downloadUrl: z
|
|
.string()
|
|
.trim()
|
|
.min(1, "请填写下载链接")
|
|
.max(2048, "下载链接不能超过 2048 个字符")
|
|
.refine((value) => !/\s/.test(value), "下载链接不能包含空白字符"),
|
|
forceUpdate: z.boolean(),
|
|
platform: z.enum(["android", "ios"]),
|
|
version: z.string().trim().min(1, "请填写版本号").max(40, "版本号不能超过 40 个字符"),
|
|
});
|
|
|
|
export type AppVersionForm = z.infer<typeof appVersionSchema>;
|