feat: 自定义版本功能更新
Build and Release / Build (push) Has been cancelled

- 新增家庭共享订阅管理
- 新增用户邀请统计
- 新增签名和订阅模式设置表单
- 更新 API 服务层和国际化文件
- UI 组件优化(enhanced-input、pro-table)
This commit is contained in:
2026-03-19 01:56:13 -07:00
parent 31803a1d24
commit 6b92979c7c
82 changed files with 3266 additions and 739 deletions
+12 -22
View File
@@ -30,7 +30,8 @@ export function EnhancedInput<T = string>({
...props
}: EnhancedInputProps<T>) {
const getProcessedValue = (inputValue: unknown) => {
if (inputValue === "" || inputValue === 0 || inputValue === "0") return "";
if (inputValue === "" || inputValue === undefined || inputValue === null)
return "";
const newValue = String(inputValue ?? "");
return formatInput ? formatInput(inputValue as T) : newValue;
};
@@ -69,13 +70,6 @@ export function EnhancedInput<T = string>({
let inputValue = e.target.value;
if (props.type === "number") {
if (inputValue === "0") {
setValue("");
setInternalValue(0);
onValueChange?.(processValue(0));
return;
}
if (
/^-?\d*\.?\d*$/.test(inputValue) ||
inputValue === "-" ||
@@ -99,7 +93,7 @@ export function EnhancedInput<T = string>({
} else {
setInternalValue(inputValue);
}
setValue(inputValue === "0" ? "" : inputValue);
setValue(inputValue);
}
} else {
setValue(inputValue);
@@ -111,19 +105,15 @@ export function EnhancedInput<T = string>({
};
const handleBlur = () => {
if (props.type === "number" && value) {
if (value === "-" || value === ".") {
setValue("");
setInternalValue("");
onValueBlur?.("" as T);
return;
}
if (value === "0") {
setValue("");
onValueBlur?.(processValue(0));
return;
}
if (
props.type === "number" &&
value !== "" &&
(value === "-" || value === ".")
) {
setValue("");
setInternalValue("");
onValueBlur?.("" as T);
return;
}
const outputValue = processValue(value);
@@ -3,6 +3,7 @@
import type { Table } from "@tanstack/react-table";
import { Input } from "@workspace/ui/components/input";
import { Combobox } from "@workspace/ui/composed/combobox";
import { useEffect, useState } from "react";
export interface IParams {
key: string;
@@ -81,15 +82,52 @@ export function ColumnFilter<TData>({
);
}
return (
<Input
className="min-w-32"
<DebouncedInput
externalValue={filters[param.key] || ""}
key={param.key}
onChange={(event) => updateFilter(param.key, event.target.value)}
onSearch={(value) => updateFilter(param.key, value)}
placeholder={param.placeholder || "Search..."}
value={filters[param.key] || ""}
/>
);
})}
</div>
);
}
function DebouncedInput({
externalValue,
onSearch,
placeholder,
}: {
externalValue: string;
onSearch: (value: string) => void;
placeholder: string;
}) {
const [localValue, setLocalValue] = useState(externalValue);
// Sync from external (e.g. reset)
useEffect(() => {
setLocalValue(externalValue);
}, [externalValue]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setLocalValue(e.target.value);
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
e.preventDefault();
onSearch(localValue);
}
};
return (
<Input
className="min-w-32"
onChange={handleChange}
onKeyDown={handleKeyDown}
placeholder={placeholder}
value={localValue}
/>
);
}
@@ -123,7 +123,7 @@ export function ProTable<
const [rowCount, setRowCount] = useState<number>(0);
const [pagination, setPagination] = useState({
pageIndex: 0,
pageSize: 10,
pageSize: 200,
});
const loading = useRef(false);
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+3 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
// API 更新时间:
// API 唯一标识:
@@ -13,6 +13,7 @@ import * as log from "./log";
import * as marketing from "./marketing";
import * as order from "./order";
import * as payment from "./payment";
import * as redemption from "./redemption";
import * as server from "./server";
import * as subscribe from "./subscribe";
import * as system from "./system";
@@ -31,6 +32,7 @@ export default {
marketing,
order,
payment,
redemption,
server,
subscribe,
system,
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+9 -3
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
@@ -56,9 +56,15 @@ export async function updateOrderStatus(
);
}
/** Manually activate order POST /v1/admin/order/activate */
/**
* 手动激活订单
* POST /v1/admin/order/activate
* @param body - 激活订单请求参数,包含 order_no
* @param options - 可选的请求配置
* @returns 激活结果
*/
export async function activateOrder(
body: { order_no: string },
body: API.ActivateOrderRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+86 -43
View File
@@ -1,43 +1,14 @@
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
/** Toggle redemption code status PUT /v1/admin/redemption/code/status */
export async function toggleRedemptionCodeStatus(
body: API.ToggleRedemptionCodeStatusRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/redemption/code/status`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/** Update redemption code PUT /v1/admin/redemption/code */
export async function updateRedemptionCode(
body: API.UpdateRedemptionCodeRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/redemption/code`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/** Create redemption code POST /v1/admin/redemption/code */
/**
* 创建兑换码
* POST /v1/admin/redemption/code
* @param body - 创建兑换码请求参数
* @param options - 可选的请求配置
* @returns 创建结果
*/
export async function createRedemptionCode(
body: API.CreateRedemptionCodeRequest,
options?: { [key: string]: any }
@@ -55,7 +26,37 @@ export async function createRedemptionCode(
);
}
/** Delete redemption code DELETE /v1/admin/redemption/code */
/**
* 更新兑换码
* PUT /v1/admin/redemption/code
* @param body - 更新兑换码请求参数
* @param options - 可选的请求配置
* @returns 更新结果
*/
export async function updateRedemptionCode(
body: API.UpdateRedemptionCodeRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/redemption/code`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 删除兑换码
* DELETE /v1/admin/redemption/code
* @param body - 删除兑换码请求参数
* @param options - 可选的请求配置
* @returns 删除结果
*/
export async function deleteRedemptionCode(
body: API.DeleteRedemptionCodeRequest,
options?: { [key: string]: any }
@@ -73,7 +74,13 @@ export async function deleteRedemptionCode(
);
}
/** Batch delete redemption code DELETE /v1/admin/redemption/code/batch */
/**
* 批量删除兑换码
* DELETE /v1/admin/redemption/code/batch
* @param body - 批量删除请求参数,包含 ids 数组
* @param options - 可选的请求配置
* @returns 批量删除结果
*/
export async function batchDeleteRedemptionCode(
body: API.BatchDeleteRedemptionCodeRequest,
options?: { [key: string]: any }
@@ -91,9 +98,15 @@ export async function batchDeleteRedemptionCode(
);
}
/** Get redemption code list GET /v1/admin/redemption/code/list */
/**
* 获取兑换码列表
* GET /v1/admin/redemption/code/list
* @param params - 分页及筛选参数
* @param options - 可选的请求配置
* @returns 兑换码列表及总数
*/
export async function getRedemptionCodeList(
params: API.GetRedemptionCodeListRequest,
params: API.GetRedemptionCodeListParams,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.GetRedemptionCodeListResponse }>(
@@ -108,9 +121,39 @@ export async function getRedemptionCodeList(
);
}
/** Get redemption record list GET /v1/admin/redemption/record/list */
/**
* 切换兑换码状态(启用/禁用)
* PUT /v1/admin/redemption/code/status
* @param body - 切换状态请求参数
* @param options - 可选的请求配置
* @returns 切换结果
*/
export async function toggleRedemptionCodeStatus(
body: API.ToggleRedemptionCodeStatusRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/redemption/code/status`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 获取兑换记录列表
* GET /v1/admin/redemption/record/list
* @param params - 分页及筛选参数
* @param options - 可选的请求配置
* @returns 兑换记录列表及总数
*/
export async function getRedemptionRecordList(
params: API.GetRedemptionRecordListRequest,
params: API.GetRedemptionRecordListParams,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.GetRedemptionRecordListResponse }>(
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+41 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
@@ -365,3 +365,43 @@ export async function updateVerifyConfig(
}
);
}
/**
* 获取签名配置
* GET /v1/admin/system/signature_config
* @param options - 可选的请求配置
* @returns 签名配置
*/
export async function getSignatureConfig(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.SignatureConfig }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/system/signature_config`,
{
method: "GET",
...(options || {}),
}
);
}
/**
* 更新签名配置
* PUT /v1/admin/system/signature_config
* @param body - 签名配置参数
* @param options - 可选的请求配置
* @returns 更新结果
*/
export async function updateSignatureConfig(
body: API.SignatureConfig,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/system/signature_config`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+183 -80
View File
@@ -250,85 +250,6 @@ declare namespace API {
enable?: boolean;
};
type CreateRedemptionCodeRequest = {
total_count: number;
subscribe_plan: number;
unit_time: string;
quantity: number;
batch_count: number;
};
type UpdateRedemptionCodeRequest = {
id: number;
total_count?: number;
subscribe_plan?: number;
unit_time?: string;
quantity?: number;
status?: number;
};
type ToggleRedemptionCodeStatusRequest = {
id: number;
status: number;
};
type DeleteRedemptionCodeRequest = {
id: number;
};
type BatchDeleteRedemptionCodeRequest = {
ids: number[];
};
type GetRedemptionCodeListRequest = {
page: number;
size: number;
subscribe_plan?: number;
unit_time?: string;
code?: string;
};
type GetRedemptionCodeListResponse = {
total: number;
list: RedemptionCode[];
};
type GetRedemptionRecordListRequest = {
page: number;
size: number;
user_id?: number;
code_id?: number;
};
type GetRedemptionRecordListResponse = {
total: number;
list: RedemptionRecord[];
};
type RedemptionCode = {
id: number;
code: string;
total_count: number;
used_count: number;
subscribe_plan: number;
unit_time: string;
quantity: number;
status: number;
created_at: number;
updated_at: number;
};
type RedemptionRecord = {
id: number;
redemption_code_id: number;
user_id: number;
subscribe_id: number;
unit_time: string;
quantity: number;
redeemed_at: number;
created_at: number;
};
type CreateDocumentRequest = {
title: string;
content: string;
@@ -1374,6 +1295,7 @@ declare namespace API {
forced_invite: boolean;
referral_percentage: number;
only_first_purchase: boolean;
gift_days?: number;
};
type KickOfflineRequest = {
@@ -1864,7 +1786,6 @@ declare namespace API {
enable_ip_register_limit: boolean;
ip_register_limit: number;
ip_register_limit_duration: number;
device_limit: number;
};
type RegisterLog = {
@@ -2659,4 +2580,186 @@ declare namespace API {
security: string;
security_config: SecurityConfig;
};
type SignatureConfig = {
enable_signature: boolean;
};
type FamilyDetail = {
summary: FamilySummary;
members: FamilyMemberItem[];
};
type FamilyMemberItem = {
user_id: number;
identifier: string;
device_no: string;
role: number;
role_name: string;
status: number;
status_name: string;
join_source: string;
joined_at: number;
left_at?: number;
};
type FamilySummary = {
family_id: number;
owner_user_id: number;
owner_identifier: string;
status: string;
active_member_count: number;
max_members: number;
created_at: number;
updated_at: number;
};
type GetFamilyDetailParams = {
id: number;
};
type GetFamilyListParams = {
page: number;
size: number;
keyword?: string;
status?: string;
owner_user_id?: number;
family_id?: number;
user_id?: number;
};
type GetFamilyListResponse = {
list: FamilySummary[];
total: number;
};
type DissolveFamilyRequest = {
family_id: number;
reason?: string;
};
type UpdateFamilyMaxMembersRequest = {
family_id: number;
max_members: number;
};
type RemoveFamilyMemberRequest = {
family_id: number;
user_id: number;
reason?: string;
};
type GetAdminUserInviteStatsParams = {
user_id: number;
};
type GetAdminUserInviteStatsResponse = {
invite_count: number;
total_commission: number;
current_commission: number;
referral_percentage: number;
only_first_purchase: boolean;
};
type GetAdminUserInviteListParams = {
user_id: number;
page: number;
size: number;
};
type AdminInvitedUser = {
id: number;
avatar: string;
identifier: string;
enable: boolean;
created_at: number;
};
type GetAdminUserInviteListResponse = {
total: number;
list: AdminInvitedUser[];
};
type ActivateOrderRequest = {
order_no: string;
};
type RedemptionCode = {
id: number;
code: string;
total_count: number;
used_count: number;
subscribe_plan: number;
unit_time: string;
quantity: number;
status: number;
created_at: number;
updated_at: number;
};
type RedemptionRecord = {
id: number;
redemption_code_id: number;
user_id: number;
subscribe_id: number;
unit_time: string;
quantity: number;
redeemed_at: number;
created_at: number;
};
type CreateRedemptionCodeRequest = {
total_count: number;
subscribe_plan: number;
unit_time: string;
quantity: number;
batch_count: number;
};
type UpdateRedemptionCodeRequest = {
id: number;
total_count?: number;
subscribe_plan?: number;
unit_time?: string;
quantity?: number;
status?: number;
};
type DeleteRedemptionCodeRequest = {
id: number;
};
type BatchDeleteRedemptionCodeRequest = {
ids: number[];
};
type GetRedemptionCodeListParams = {
page: number;
size: number;
subscribe_plan?: number;
unit_time?: string;
code?: string;
};
type GetRedemptionCodeListResponse = {
total: number;
list: RedemptionCode[];
};
type ToggleRedemptionCodeStatusRequest = {
id: number;
status: number;
};
type GetRedemptionRecordListParams = {
page: number;
size: number;
user_id?: number;
code_id?: number;
};
type GetRedemptionRecordListResponse = {
total: number;
list: RedemptionRecord[];
};
}
+167 -27
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
@@ -291,32 +291,6 @@ export async function getUserSubscribe(
params: {
...params,
},
transformResponse: [
(data) => {
try {
if (typeof data === "string") {
// Convert large integers (int64) to strings BEFORE JSON.parse to prevent precision loss
// JavaScript MAX_SAFE_INTEGER is 2^53 - 1 = 9007199254740991
// This regex finds all numbers >= 10^16 (larger than MAX_SAFE_INTEGER)
const processedData = data.replace(
/"([^"]+)":\s*(\d{16,})/g,
(match, key, value) => {
// Check if number exceeds MAX_SAFE_INTEGER
const num = parseInt(value, 10);
if (!Number.isSafeInteger(num)) {
return `"${key}": "${value}"`;
}
return match;
}
);
return JSON.parse(processedData);
}
return data;
} catch {
return data;
}
},
],
...(options || {}),
}
);
@@ -531,3 +505,169 @@ export async function getUserSubscribeTrafficLogs(
}
);
}
/**
* 获取家庭组详情
* GET /v1/admin/user/family/detail
* @param params - 家庭组 ID
* @param options - 可选的请求配置
* @returns 家庭组详情
*/
export async function getFamilyDetail(
params: API.GetFamilyDetailParams,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.FamilyDetail }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/user/family/detail`,
{
method: "GET",
params: {
...params,
},
...(options || {}),
}
);
}
/**
* 获取家庭组列表
* GET /v1/admin/user/family/list
* @param params - 分页及筛选参数
* @param options - 可选的请求配置
* @returns 家庭组列表
*/
export async function getFamilyList(
params: API.GetFamilyListParams,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.GetFamilyListResponse }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/user/family/list`,
{
method: "GET",
params: {
...params,
},
...(options || {}),
}
);
}
/**
* 解散家庭组
* PUT /v1/admin/user/family/dissolve
* @param body - 解散请求参数
* @param options - 可选的请求配置
* @returns 解散结果
*/
export async function dissolveFamily(
body: API.DissolveFamilyRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/user/family/dissolve`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 更新家庭组最大成员数
* PUT /v1/admin/user/family/max_members
* @param body - 更新参数
* @param options - 可选的请求配置
* @returns 更新结果
*/
export async function updateFamilyMaxMembers(
body: API.UpdateFamilyMaxMembersRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/user/family/max_members`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 移除家庭组成员
* PUT /v1/admin/user/family/member/remove
* @param body - 移除成员请求参数
* @param options - 可选的请求配置
* @returns 移除结果
*/
export async function removeFamilyMember(
body: API.RemoveFamilyMemberRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${
import.meta.env.VITE_API_PREFIX || ""
}/v1/admin/user/family/member/remove`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 获取管理员用户邀请统计
* GET /v1/admin/user/invite/stats
* @param params - 用户 ID
* @param options - 可选的请求配置
* @returns 邀请统计数据
*/
export async function getAdminUserInviteStats(
params: API.GetAdminUserInviteStatsParams,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.GetAdminUserInviteStatsResponse }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/user/invite/stats`,
{
method: "GET",
params: {
...params,
},
...(options || {}),
}
);
}
/**
* 获取管理员用户邀请列表
* GET /v1/admin/user/invite/list
* @param params - 用户 ID 及分页参数
* @param options - 可选的请求配置
* @returns 邀请用户列表
*/
export async function getAdminUserInviteList(
params: API.GetAdminUserInviteListParams,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.GetAdminUserInviteListResponse }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/user/invite/list`,
{
method: "GET",
params: {
...params,
},
...(options || {}),
}
);
}
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
// API 更新时间:
// API 唯一标识:
+2 -4
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
@@ -19,9 +19,7 @@ export async function appleLoginCallback(
if (item !== undefined && item !== null) {
if (typeof item === "object" && !(item instanceof File)) {
if (Array.isArray(item)) {
item.forEach((f) => {
formData.append(ele, f || "");
});
for (const f of item) formData.append(ele, f || "");
} else {
formData.append(
ele,
+1
View File
@@ -356,6 +356,7 @@ declare namespace API {
forced_invite: boolean;
referral_percentage: number;
only_first_purchase: boolean;
gift_days?: number;
};
type LoginResponse = {
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
// API 更新时间:
// API 唯一标识:
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
// API 更新时间:
// API 唯一标识:
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1 -1
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
+1
View File
@@ -373,6 +373,7 @@ declare namespace API {
forced_invite: boolean;
referral_percentage: number;
only_first_purchase: boolean;
gift_days?: number;
};
type MessageLog = {
+1 -19
View File
@@ -1,4 +1,4 @@
// @ts-nocheck
// @ts-expect-error
/* eslint-disable */
import request from "@workspace/ui/lib/request";
@@ -269,24 +269,6 @@ export async function updateUserRules(
);
}
/** Redeem Code POST /v1/public/redemption/ */
export async function redeemCode(
body: { code: string },
options?: { [key: string]: any }
) {
return request<API.Response & { data: { message: string } }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/public/redemption/`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/** Query User Subscribe GET /v1/public/user/subscribe */
export async function queryUserSubscribe(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.QueryUserSubscribeListResponse }>(
+33
View File
@@ -0,0 +1,33 @@
const DEVICE_HASH_SALT = 0x5a_3c_7e_9b;
/**
* Encode device id (user_device.id) to 8-char hex hash (bidirectional)
* e.g. 1 → "5A3C7E9A", 42 → "5A3C7EA1"
*/
export function deviceIdToHash(id: number): string {
// biome-ignore lint/suspicious/noBitwiseOperators: intentional XOR hash
return ((id ^ DEVICE_HASH_SALT) >>> 0)
.toString(16)
.toUpperCase()
.padStart(8, "0");
}
/**
* Decode 8-char hex hash back to device id
* e.g. "5A3C7E9A" → 1
*/
export function hashToDeviceId(hash: string): number {
// biome-ignore lint/suspicious/noBitwiseOperators: intentional XOR hash
return (Number.parseInt(hash, 16) ^ DEVICE_HASH_SALT) >>> 0;
}
/**
* Shorten a long device identifier (e.g. "device68c71ab9f82d...") to 8-char display
* Used when only the identifier string is available (no numeric device id)
*/
export function shortenDeviceIdentifier(identifier: string): string {
return identifier
.replace(/^device/i, "")
.slice(0, 8)
.toUpperCase();
}