import { z } from "zod"; const commandBaseSchema = z.object({ commandId: z.string().trim().min(1, "请输入 Command ID").max(120, "Command ID 不能超过 120 个字符"), reason: z.string().trim().max(200, "原因不能超过 200 个字符").optional().default(""), }); const commandContactSchema = z.object({ commandId: z.string().trim().min(1, "请输入 Command ID").max(120, "Command ID 不能超过 120 个字符"), contact: z.string().trim().max(128, "联系方式不能超过 128 个字符").optional().default(""), reason: z.string().trim().max(200, "原因不能超过 200 个字符").optional().default(""), }); const userIdSchema = z.coerce.number().int().positive("请输入有效用户短 ID"); const optionalUserIdSchema = z.preprocess( (value) => { if (value === null || value === undefined) { return 0; } if (typeof value === "string" && value.trim() === "") { return 0; } return value; }, z.coerce.number().int().min(0, "请输入有效用户短 ID"), ); const regionIdSchema = z.coerce.number().int().positive("请输入有效区域 ID"); const sortOrderSchema = z.coerce.number().int().min(0, "排序不能小于 0").default(0); const optionalTextSchema = (max: number, message: string) => z.string().trim().max(max, message).optional().default(""); const codeSchema = (label: string) => z.string().trim().min(1, `请输入${label}`).max(40, `${label}不能超过 40 个字符`); const countryCodesSchema = z.array(z.string()).transform((values, ctx) => { const countries = values.map((item) => item.trim().toUpperCase()).filter(Boolean); if (new Set(countries).size !== countries.length) { ctx.addIssue({ code: "custom", message: "国家码不能重复" }); } return countries; }); const retiredRegionCodes = new Set([ "AUSTRALIA AND NEW ZEALAND", "CARIBBEAN", "MELANESIA", "MICRONESIA", "POLYNESIA", "SOUTHERN AFRICA", "UNSPECIFIED", ]); const requiredCountryCodesSchema = countryCodesSchema.superRefine((countries, ctx) => { if (!countries.length) { ctx.addIssue({ code: "custom", message: "请选择国家码" }); } }); const regionBaseSchema = z .object({ countries: countryCodesSchema, name: z.string().trim().min(1, "请输入区域名称").max(80, "区域名称不能超过 80 个字符"), regionCode: codeSchema("区域编码").transform((value) => value.toUpperCase()), sortOrder: sortOrderSchema, }) .superRefine((value, ctx) => { if (retiredRegionCodes.has(value.regionCode)) { ctx.addIssue({ code: "custom", message: "该区域已移除", path: ["regionCode"] }); } if (value.regionCode !== "GLOBAL" && !value.countries.length) { ctx.addIssue({ code: "custom", message: "请选择国家码", path: ["countries"] }); } if (value.regionCode === "GLOBAL" && value.countries.length) { ctx.addIssue({ code: "custom", message: "GLOBAL 区域不需要选择国家", path: ["countries"] }); } }); export const createBDLeaderSchema = commandContactSchema.extend({ positionAlias: optionalTextSchema(64, "职位别名不能超过 64 个字符"), targetUserId: userIdSchema, }); export const createBDSchema = commandBaseSchema.extend({ parentLeaderUserId: optionalUserIdSchema, targetUserId: userIdSchema, }); export const bdStatusSchema = commandBaseSchema.extend({ status: z.string().trim().min(1, "请输入状态").max(40, "状态不能超过 40 个字符"), targetType: z.enum(["bd", "leader"]), targetUserId: userIdSchema, }); export const createCoinSellerSchema = commandContactSchema.extend({ targetUserId: userIdSchema, }); export const coinSellerStatusSchema = commandContactSchema.extend({ status: z.enum(["active", "disabled"]), targetUserId: userIdSchema, }); export const coinSellerStockCreditSchema = z .object({ coinAmount: z.coerce.number().int().positive("请输入充值金币"), commandId: z.string().trim().min(1, "请输入 Command ID").max(120, "Command ID 不能超过 120 个字符"), reason: z.string().trim().max(512, "原因不能超过 512 个字符").optional().default(""), rechargeAmount: z.string().trim().max(32, "充值金额不能超过 32 个字符").optional().default(""), type: z.enum(["usdt_purchase", "coin_compensation"]), }) .superRefine((value, ctx) => { if (value.type === "usdt_purchase") { if (!/^(0|[1-9]\d*)(\.\d{1,6})?$/.test(value.rechargeAmount) || Number(value.rechargeAmount) <= 0) { ctx.addIssue({ code: "custom", message: "请输入最多 6 位小数的 USDT 金额", path: ["rechargeAmount"] }); } return; } if (value.rechargeAmount) { ctx.addIssue({ code: "custom", message: "金币补偿不能提交充值金额", path: ["rechargeAmount"] }); } }); export const createAgencySchema = commandBaseSchema.extend({ joinEnabled: z.boolean().default(true), name: z.string().trim().min(1, "请输入 Agency 名称").max(80, "Agency 名称不能超过 80 个字符"), ownerUserId: userIdSchema, parentBdUserId: optionalUserIdSchema, }); export const agencyCloseSchema = commandBaseSchema.extend({ agencyId: z.coerce.number().int().positive("请输入有效 Agency ID"), }); export const agencyJoinEnabledSchema = commandBaseSchema.extend({ agencyId: z.coerce.number().int().positive("请输入有效 Agency ID"), joinEnabled: z.boolean().default(true), }); export const countryCreateSchema = z.object({ countryCode: z .string() .trim() .regex(/^[A-Za-z]{2,3}$/, "国家码必须是 2-3 位字母") .transform((value) => value.toUpperCase()), countryDisplayName: z.string().trim().min(1, "请输入展示名称").max(80, "展示名称不能超过 80 个字符"), countryName: z.string().trim().min(1, "请输入国家名称").max(80, "国家名称不能超过 80 个字符"), enabled: z.boolean().default(true), flag: optionalTextSchema(16, "国旗不能超过 16 个字符"), isoAlpha3: optionalTextSchema(8, "ISO Alpha-3 不能超过 8 个字符").transform((value) => value.toUpperCase()), isoNumeric: optionalTextSchema(8, "ISO Numeric 不能超过 8 个字符"), phoneCountryCode: optionalTextSchema(16, "电话区号不能超过 16 个字符"), sortOrder: sortOrderSchema, }); export const countryUpdateSchema = countryCreateSchema.omit({ countryCode: true, enabled: true, }); export const regionCreateSchema = regionBaseSchema; export const regionUpdateSchema = regionBaseSchema; export const regionCountriesSchema = z.object({ countries: requiredCountryCodesSchema, }); export type CreateBDLeaderForm = z.infer; export type CreateBDForm = z.infer; export type BDStatusForm = z.infer; export type CreateCoinSellerForm = z.infer; export type CoinSellerStatusForm = z.infer; export type CoinSellerStockCreditForm = z.infer; export type CreateAgencyForm = z.infer; export type AgencyCloseForm = z.infer; export type AgencyJoinEnabledForm = z.infer; export type CountryCreateForm = z.infer; export type CountryUpdateForm = z.infer; export type RegionCreateForm = z.infer; export type RegionUpdateForm = z.infer; export type RegionCountriesForm = z.infer;