diff --git a/h5/hyapp/manager-center/index.html b/h5/hyapp/manager-center/index.html index 0743392..4a92724 100644 --- a/h5/hyapp/manager-center/index.html +++ b/h5/hyapp/manager-center/index.html @@ -1,11 +1,97 @@ - - - - - - Document - - - manager-center - - \ No newline at end of file + + + + + + 经理中心 + + + +
+ + +
+
+
+ + M +
+
+
当前经理
+
-
+
UID: -
+
+
+ +
+
+

用户

+
+
+ + +
+ +
+ + + +
+
+

赠送道具

+
+ + + +
+ +
+ + +
+ +
+ + 加载中... +
+ +
+ + + diff --git a/h5/hyapp/manager-center/script.js b/h5/hyapp/manager-center/script.js new file mode 100644 index 0000000..4352f3d --- /dev/null +++ b/h5/hyapp/manager-center/script.js @@ -0,0 +1,471 @@ +(function () { + const API_BASE_URL = "https://jvapi.haiyihy.com"; + const GO_API_PATH_PREFIX = "/go"; + const LOCAL_GO_API_BASE_URL = "http://127.0.0.1:1100"; + const defaultPropsType = "RIDE"; + + const state = { + manager: null, + target: null, + propsType: defaultPropsType, + props: [], + selectedPropsId: "" + }; + + const params = readURLParams(); + + function readURLParams() { + const result = new URLSearchParams(window.location.search); + const hashQuery = window.location.hash.includes("?") ? window.location.hash.split("?")[1] : ""; + const hashParams = new URLSearchParams(hashQuery); + hashParams.forEach((value, key) => { + if (!result.has(key)) result.set(key, value); + }); + return result; + } + + function readRawParam(name) { + const queries = [window.location.search.replace(/^\?/, "")]; + if (window.location.hash.includes("?")) queries.push(window.location.hash.split("?")[1]); + + for (const query of queries) { + for (const part of query.split("&")) { + if (!part) continue; + const equalIndex = part.indexOf("="); + const rawKey = equalIndex >= 0 ? part.slice(0, equalIndex) : part; + const rawValue = equalIndex >= 0 ? part.slice(equalIndex + 1) : ""; + let decodedKey = ""; + try { + decodedKey = decodeURIComponent(rawKey); + } catch (error) { + continue; + } + if (decodedKey !== name) continue; + try { + return decodeURIComponent(rawValue.replace(/\+/g, "%2B")); + } catch (error) { + return rawValue; + } + } + } + return ""; + } + + function $(selector) { + return document.querySelector(selector); + } + + function apiBaseURL() { + const override = window.HYAPP_API_BASE_URL || params.get("apiBase"); + if (override) return String(override).replace(/\/+$/, ""); + return API_BASE_URL.replace(/\/+$/, ""); + } + + function isLocalDocumentsFile() { + if (window.location.protocol !== "file:") return false; + let pathname = window.location.pathname || ""; + try { + pathname = decodeURIComponent(pathname); + } catch (error) { + return false; + } + return pathname.startsWith("/Users/hy/Documents/"); + } + + function goApiBaseURL() { + const override = window.HYAPP_GO_API_BASE_URL || params.get("goApiBase") || params.get("goBase"); + if (override) return String(override).replace(/\/+$/, ""); + if (isLocalDocumentsFile()) return `${LOCAL_GO_API_BASE_URL}${GO_API_PATH_PREFIX}`; + return `${apiBaseURL()}${GO_API_PATH_PREFIX}`; + } + + function buildRequestURL(baseURL, path) { + const base = `${String(baseURL || API_BASE_URL).replace(/\/+$/, "")}/`; + const target = String(path || "").replace(/^\/+/, ""); + return new URL(target, base).toString(); + } + + function normalizeAuthorization(token) { + const value = String(token || "").trim(); + if (!value) return ""; + return /^bearer\s+/i.test(value) ? value : `Bearer ${value}`; + } + + function authHeadersFromToken(token) { + const sysOrigin = params.get("sysOrigin") || params.get("origin") || "ATYOU"; + return { + Authorization: normalizeAuthorization(token), + "req-lang": params.get("lang") || "zh-CN", + "req-client": "H5", + "req-version": "V2", + "req-zone": Intl.DateTimeFormat().resolvedOptions().timeZone || "Asia/Shanghai", + "req-app-intel": "version=1.0.0;build=1;model=H5;sysVersion=Web;channel=Web", + "req-sys-origin": `origin=${sysOrigin};originChild=${sysOrigin}` + }; + } + + async function requestGoJSON(path, options = {}) { + const token = readRawParam("token"); + if (!token) { + const error = new Error("缺少登录信息"); + error.code = "missing_token"; + throw error; + } + + const headers = authHeadersFromToken(token); + const request = { + method: options.method || "GET", + cache: "no-store", + headers + }; + if (options.body !== undefined) { + headers["Content-Type"] = "application/json"; + request.body = JSON.stringify(options.body); + } + + const response = await fetch(buildRequestURL(goApiBaseURL(), path), request); + const data = await response.json().catch(() => ({})); + if (!response.ok || data.status === false) { + const error = new Error(data.errorMsg || data.message || `Request failed: ${response.status}`); + error.code = data.code || data.errorCodeName || ""; + error.response = data; + throw error; + } + return data.body ?? data.data ?? null; + } + + function endpointProfile() { + return "/app/h5/manager-center/profile"; + } + + function endpointSearchUser(account) { + return `/app/h5/manager-center/users/search?account=${encodeURIComponent(account)}`; + } + + function endpointProps(type) { + return `/app/h5/manager-center/props?type=${encodeURIComponent(type || "")}`; + } + + function endpointSendProps() { + return "/app/h5/manager-center/props/send"; + } + + function endpointBanUser() { + return "/app/h5/manager-center/users/ban"; + } + + function setLoading(isLoading) { + const root = $(".manager-center"); + const mask = $("#loadingMask"); + if (root) root.dataset.loading = isLoading ? "true" : "false"; + if (mask) mask.hidden = !isLoading; + } + + function setStatus(node, text, type = "") { + if (!node) return; + node.textContent = text || ""; + node.hidden = !text; + node.classList.toggle("is-error", type === "error"); + } + + function showToast(text) { + const toast = $("#toast"); + if (!toast) return; + toast.textContent = text || ""; + toast.hidden = !text; + window.clearTimeout(showToast.timer); + showToast.timer = window.setTimeout(() => { + toast.hidden = true; + }, 2300); + } + + function closePage() { + try { + if (window.app && typeof window.app.closePage === "function") { + window.app.closePage(); + return; + } + if (window.FlutterPageControl && typeof window.FlutterPageControl.postMessage === "function") { + window.FlutterPageControl.postMessage("close_page"); + return; + } + if (window.history.length > 1) window.history.back(); + } catch (error) { + console.error("close manager center failed:", error); + if (window.history.length > 1) window.history.back(); + } + } + + function firstLetter(name, fallback) { + const value = String(name || fallback || "U").trim(); + return value ? value.slice(0, 1).toUpperCase() : "U"; + } + + function renderAvatar(imgSelector, fallbackSelector, url, name, fallback) { + const img = $(imgSelector); + const fallbackNode = $(fallbackSelector); + if (!img || !fallbackNode) return; + const src = String(url || "").trim(); + fallbackNode.textContent = firstLetter(name, fallback); + if (src) { + img.src = src; + img.hidden = false; + fallbackNode.hidden = true; + } else { + img.removeAttribute("src"); + img.hidden = true; + fallbackNode.hidden = false; + } + } + + function displayName(user) { + return user?.userNickname || user?.nickname || user?.name || "-"; + } + + function displayUserID(user) { + return String(user?.userId || user?.id || "-"); + } + + function displayAccount(user) { + return user?.account ? ` (${user.account})` : ""; + } + + function canBanTarget(target) { + return Boolean(target) && target.canBan !== false && !target.hasRecharge; + } + + function renderManager(manager) { + $("#managerName").textContent = displayName(manager); + $("#managerUid").textContent = `UID: ${displayUserID(manager)}${displayAccount(manager)}`; + renderAvatar("#managerAvatar", "#managerAvatarFallback", manager?.userAvatar, displayName(manager), "M"); + } + + function renderTarget() { + const card = $("#targetCard"); + const banCard = $("#banCard"); + if (!state.target) { + if (card) card.hidden = true; + if (banCard) banCard.hidden = true; + updateActionState(); + return; + } + + const target = state.target; + card.hidden = false; + banCard.hidden = false; + $("#targetName").textContent = displayName(target); + $("#targetUid").textContent = `UID: ${displayUserID(target)}${displayAccount(target)}`; + $("#targetAccountStatus").textContent = target.accountStatus || "-"; + const rechargeStatus = $("#targetRechargeStatus"); + const canBan = canBanTarget(target); + rechargeStatus.textContent = target.hasRecharge ? "已充值" : (canBan ? "未充值" : "充值状态未知"); + rechargeStatus.classList.toggle("is-danger", Boolean(target.hasRecharge) || !canBan); + const banEligibility = $("#banEligibility"); + banEligibility.textContent = canBan ? "可封禁" : "不可封禁"; + banEligibility.classList.toggle("is-danger", !canBan); + renderAvatar("#targetAvatar", "#targetAvatarFallback", target.userAvatar, displayName(target), "U"); + updateActionState(); + } + + function renderProps() { + const list = $("#propsList"); + if (!list) return; + list.replaceChildren(); + if (!state.props.length) { + setStatus($("#propsStatus"), "没有可赠送道具"); + updateActionState(); + return; + } + setStatus($("#propsStatus"), ""); + + state.props.forEach((item) => { + const button = document.createElement("button"); + button.type = "button"; + button.className = "prop-option"; + button.dataset.propsId = String(item.id); + if (String(item.id) === String(state.selectedPropsId)) { + button.classList.add("is-selected"); + } + + const cover = document.createElement("span"); + cover.className = "prop-cover"; + if (item.cover || item.sourceUrl) { + const img = document.createElement("img"); + img.alt = ""; + img.src = item.cover || item.sourceUrl; + cover.appendChild(img); + } else { + const fallback = document.createElement("span"); + fallback.textContent = firstLetter(item.name, "G"); + cover.appendChild(fallback); + } + + const copy = document.createElement("span"); + copy.className = "prop-copy"; + const name = document.createElement("span"); + name.className = "prop-name"; + name.textContent = item.name || `ID ${item.id}`; + const meta = document.createElement("span"); + meta.className = "prop-meta"; + meta.textContent = item.vipLevel ? `${item.type} · VIP ${item.vipLevel}` : item.type; + copy.append(name, meta); + + const days = document.createElement("span"); + days.className = "prop-days"; + days.textContent = `${item.days || 0}天`; + + button.append(cover, copy, days); + button.addEventListener("click", () => { + state.selectedPropsId = String(item.id); + renderProps(); + updateActionState(); + }); + list.appendChild(button); + }); + updateActionState(); + } + + function updateActionState() { + const sendButton = $("#sendPropsButton"); + const banButton = $("#banButton"); + if (sendButton) sendButton.disabled = !state.target || !state.selectedPropsId; + if (banButton) banButton.disabled = !canBanTarget(state.target); + } + + async function loadProfile() { + const body = await requestGoJSON(endpointProfile()); + state.manager = body?.manager || body || {}; + renderManager(state.manager); + } + + async function loadProps() { + setStatus($("#propsStatus"), "加载中..."); + state.props = []; + state.selectedPropsId = ""; + renderProps(); + + const body = await requestGoJSON(endpointProps(state.propsType)); + state.props = Array.isArray(body?.records) ? body.records : []; + state.selectedPropsId = state.props.length ? String(state.props[0].id) : ""; + renderProps(); + } + + async function searchUser(event) { + event.preventDefault(); + const input = $("#accountInput"); + const account = String(input?.value || "").trim(); + if (!account) { + setStatus($("#searchStatus"), "请输入用户 ID 或账号", "error"); + return; + } + + $("#searchButton").disabled = true; + setStatus($("#searchStatus"), "搜索中..."); + try { + const body = await requestGoJSON(endpointSearchUser(account)); + state.target = body?.user || body || null; + renderTarget(); + setStatus($("#searchStatus"), ""); + } catch (error) { + state.target = null; + renderTarget(); + setStatus($("#searchStatus"), friendlyError(error), "error"); + } finally { + $("#searchButton").disabled = false; + } + } + + async function sendProps() { + if (!state.target || !state.selectedPropsId) return; + if (!window.confirm("确认赠送该道具?")) return; + + const button = $("#sendPropsButton"); + button.disabled = true; + try { + await requestGoJSON(endpointSendProps(), { + method: "POST", + body: { + acceptUserId: displayUserID(state.target), + propsId: state.selectedPropsId + } + }); + showToast("赠送成功"); + } catch (error) { + showToast(friendlyError(error)); + } finally { + updateActionState(); + } + } + + async function banUser() { + if (!canBanTarget(state.target)) return; + if (!window.confirm("确认封禁该用户?")) return; + + const button = $("#banButton"); + button.disabled = true; + setStatus($("#banStatus"), "提交中..."); + try { + await requestGoJSON(endpointBanUser(), { + method: "POST", + body: { + userId: displayUserID(state.target), + reason: $("#banReason").value || "" + } + }); + state.target.accountStatus = "ARCHIVE"; + renderTarget(); + setStatus($("#banStatus"), ""); + showToast("封禁成功"); + } catch (error) { + setStatus($("#banStatus"), friendlyError(error), "error"); + } finally { + updateActionState(); + } + } + + function friendlyError(error) { + const code = error?.code || error?.response?.code || error?.response?.errorCodeName || ""; + if (code === "missing_token") return "缺少登录信息"; + if (code === "not_manager") return "当前账号不是经理"; + if (code === "user_not_found") return "用户不存在"; + if (code === "user_recharged") return "该用户已有充值记录,不能封禁"; + if (code === "prop_not_giftable") return "该道具未开放经理赠送"; + if (code === "vip_not_giftable") return "该贵族道具不可赠送"; + return error?.message || "请求失败"; + } + + function bindEvents() { + $(".back-button")?.addEventListener("click", closePage); + $("#searchForm")?.addEventListener("submit", searchUser); + $("#propsTypeSelect")?.addEventListener("change", async (event) => { + state.propsType = event.target.value || defaultPropsType; + try { + await loadProps(); + } catch (error) { + setStatus($("#propsStatus"), friendlyError(error), "error"); + } + }); + $("#sendPropsButton")?.addEventListener("click", sendProps); + $("#banButton")?.addEventListener("click", banUser); + } + + async function init() { + bindEvents(); + $("#propsTypeSelect").value = state.propsType; + if (!readRawParam("token")) { + setLoading(false); + setStatus($("#searchStatus"), "缺少登录信息", "error"); + return; + } + + try { + await Promise.all([loadProfile(), loadProps()]); + } catch (error) { + showToast(friendlyError(error)); + } finally { + setLoading(false); + } + } + + init(); +})(); diff --git a/h5/hyapp/manager-center/style.css b/h5/hyapp/manager-center/style.css new file mode 100644 index 0000000..9288e15 --- /dev/null +++ b/h5/hyapp/manager-center/style.css @@ -0,0 +1,460 @@ +:root { + --page-bg: #f5f6f8; + --card-bg: #ffffff; + --text: #20242a; + --muted: #7c8592; + --line: #e7ebf0; + --primary: #18b89f; + --primary-dark: #0b8b79; + --danger: #d83c3c; + --danger-bg: #fff0f0; + --gold: #c79633; + --shadow: 0 10px 28px rgba(31, 41, 55, 0.08); +} + +* { + box-sizing: border-box; +} + +html, +body { + width: 100%; + min-height: 100%; + margin: 0; + -webkit-font-smoothing: antialiased; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: transparent; +} + +body { + display: flex; + justify-content: center; + background: #16191d; + color: var(--text); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; +} + +button, +input, +select, +textarea { + font: inherit; +} + +button { + border: 0; +} + +button:disabled { + cursor: not-allowed; + opacity: 0.48; +} + +[hidden] { + display: none !important; +} + +.manager-center { + position: relative; + width: 100%; + max-width: 430px; + min-height: 100vh; + overflow: hidden; + background: + linear-gradient(180deg, #eaf7f4 0, #f5f6f8 210px), + var(--page-bg); +} + +.manager-center[data-loading="true"] .title-bar, +.manager-center[data-loading="true"] .content { + visibility: hidden; +} + +.title-bar { + position: relative; + z-index: 2; + display: grid; + grid-template-columns: 48px 1fr 48px; + align-items: center; + min-height: 60px; + padding: env(safe-area-inset-top) 14px 0; +} + +.title-bar h1 { + margin: 0; + text-align: center; + font-size: 20px; + font-weight: 800; + letter-spacing: 0; +} + +.back-button { + display: inline-flex; + align-items: center; + justify-content: center; + width: 38px; + height: 38px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.76); + color: var(--text); + box-shadow: 0 4px 14px rgba(32, 36, 42, 0.08); +} + +.back-button svg { + width: 26px; + height: 26px; + fill: none; + stroke: currentColor; + stroke-width: 3; + stroke-linecap: round; + stroke-linejoin: round; +} + +.content { + position: relative; + z-index: 1; + display: flex; + flex-direction: column; + gap: 12px; + padding: 12px 14px calc(28px + env(safe-area-inset-bottom)); +} + +.card { + border: 1px solid rgba(231, 235, 240, 0.9); + border-radius: 8px; + background: var(--card-bg); + box-shadow: var(--shadow); +} + +.profile-card, +.target-card { + display: flex; + gap: 12px; + align-items: center; + min-height: 96px; + padding: 14px; +} + +.avatar-shell { + position: relative; + flex: 0 0 58px; + width: 58px; + height: 58px; + overflow: hidden; + border-radius: 50%; + background: #dfe7ea; +} + +.avatar-shell img, +.avatar-shell span { + display: block; + width: 100%; + height: 100%; +} + +.avatar-shell img { + object-fit: cover; +} + +.avatar-shell span { + display: flex; + align-items: center; + justify-content: center; + color: #51606d; + font-size: 24px; + font-weight: 800; +} + +.profile-copy { + min-width: 0; +} + +.eyebrow { + margin-bottom: 4px; + color: var(--primary-dark); + font-size: 12px; + font-weight: 800; +} + +.name { + overflow: hidden; + color: var(--text); + font-size: 17px; + font-weight: 800; + text-overflow: ellipsis; + white-space: nowrap; +} + +.meta { + margin-top: 3px; + color: var(--muted); + font-size: 13px; +} + +.pill-row { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 8px; +} + +.status-pill, +.danger-chip { + display: inline-flex; + align-items: center; + min-height: 24px; + border-radius: 999px; + padding: 0 9px; + background: #edf5f2; + color: #315e56; + font-size: 12px; + font-weight: 800; +} + +.status-pill.is-danger, +.danger-chip.is-danger { + background: var(--danger-bg); + color: var(--danger); +} + +.search-card, +.gift-card, +.ban-card { + padding: 14px; +} + +.section-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + margin-bottom: 12px; +} + +.section-head h2 { + margin: 0; + font-size: 16px; + font-weight: 900; + letter-spacing: 0; +} + +.search-form { + display: grid; + grid-template-columns: 1fr 88px; + gap: 8px; +} + +input, +select, +textarea { + width: 100%; + border: 1px solid var(--line); + border-radius: 8px; + outline: none; + background: #f9fafb; + color: var(--text); +} + +input, +select { + height: 44px; + padding: 0 12px; +} + +textarea { + min-height: 84px; + resize: none; + padding: 12px; +} + +input:focus, +select:focus, +textarea:focus { + border-color: var(--primary); + background: #fff; + box-shadow: 0 0 0 3px rgba(24, 184, 159, 0.13); +} + +.search-form button, +.primary-action, +.danger-action { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 44px; + border-radius: 8px; + padding: 0 16px; + color: #fff; + font-weight: 900; +} + +.search-form button, +.primary-action { + background: var(--primary); +} + +.danger-action { + width: 100%; + margin-top: 10px; + background: var(--danger); +} + +.primary-action { + width: 100%; + margin-top: 12px; +} + +.field-label { + display: block; + margin-bottom: 8px; + color: var(--muted); + font-size: 13px; + font-weight: 800; +} + +.props-list { + display: grid; + gap: 8px; + margin-top: 12px; +} + +.prop-option { + display: grid; + grid-template-columns: 52px 1fr auto; + align-items: center; + gap: 10px; + width: 100%; + min-height: 68px; + border: 1px solid var(--line); + border-radius: 8px; + padding: 8px; + background: #fff; + color: inherit; + text-align: left; +} + +.prop-option.is-selected { + border-color: var(--primary); + box-shadow: 0 0 0 3px rgba(24, 184, 159, 0.13); +} + +.prop-cover { + width: 52px; + height: 52px; + overflow: hidden; + border-radius: 8px; + background: #eef2f5; +} + +.prop-cover img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; +} + +.prop-cover span { + display: flex; + width: 100%; + height: 100%; + align-items: center; + justify-content: center; + color: var(--gold); + font-weight: 900; +} + +.prop-copy { + min-width: 0; +} + +.prop-name { + overflow: hidden; + font-size: 15px; + font-weight: 900; + text-overflow: ellipsis; + white-space: nowrap; +} + +.prop-meta { + margin-top: 4px; + color: var(--muted); + font-size: 12px; +} + +.prop-days { + border-radius: 999px; + padding: 5px 8px; + background: #f2f4f7; + color: #596273; + font-size: 12px; + font-weight: 900; +} + +.form-status, +.props-status { + margin-top: 10px; + border-radius: 8px; + padding: 9px 10px; + background: #f1f5f9; + color: #475569; + font-size: 13px; + font-weight: 700; +} + +.form-status.is-error, +.props-status.is-error { + background: var(--danger-bg); + color: var(--danger); +} + +.loading-mask { + position: absolute; + z-index: 20; + inset: 0; + display: flex; + min-height: 100vh; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 14px; + background: rgba(245, 246, 248, 0.96); + color: var(--primary-dark); + font-weight: 900; +} + +.loader { + width: 34px; + height: 34px; + border: 4px solid rgba(24, 184, 159, 0.18); + border-top-color: var(--primary); + border-radius: 50%; + animation: spin 0.82s linear infinite; +} + +.toast { + position: fixed; + z-index: 40; + left: 50%; + bottom: calc(24px + env(safe-area-inset-bottom)); + max-width: min(330px, calc(100vw - 40px)); + transform: translateX(-50%); + border-radius: 8px; + padding: 10px 14px; + background: rgba(29, 34, 40, 0.92); + color: #fff; + font-size: 13px; + font-weight: 800; + text-align: center; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +@media (min-width: 720px) { + .manager-center { + min-height: 760px; + } +} diff --git a/h5/laludelete-account.html b/h5/laludelete-account.html new file mode 100644 index 0000000..c8bf00c --- /dev/null +++ b/h5/laludelete-account.html @@ -0,0 +1,39 @@ + + + + + + Delete Lalu Voice Party Account | Lalu Voice Party + + + + +

Delete Lalu Voice Party Account

+

Effective date: 2026-04-13

+

Contact: chekrindo@gmail.com / +85247410298

+

Start a deletion request by email

+

This page explains how Lalu Voice Party users can request permanent deletion of their account and associated data.

+ +

1. Who can use this page

+

Use this page if you created a Lalu Voice Party account and want to request permanent deletion of that account and the associated user data managed by Lalu Voice Party.

You can submit a deletion request even if you no longer have access to the Lalu Voice Party app on your device.

+ +

2. How to submit a deletion request

+

Send your deletion request by email to chekrindo@gmail.com.

Please use the subject line: Delete Lalu Voice Party Account.

+

- Your Lalu Voice Party UID, if available.

- Your registered email address or phone number.

- Your Lalu Voice Party nickname or profile name.

- A clear statement that you want your Lalu Voice Party account and associated data to be deleted.

+ +

3. Identity verification

+

To protect account security and prevent unauthorized deletion, we may ask you to complete a reasonable identity-verification step before processing your request.

+ +

4. What will be deleted

+

After your request is verified and approved, we will delete your Lalu Voice Party account and the associated account data that is managed in our systems, including profile information and other user data linked to that account, unless retention is required for legitimate reasons described below.

+ +

5. Data that may be retained

+

Certain records may be retained for a limited period where reasonably necessary for fraud prevention, abuse handling, dispute resolution, financial recordkeeping, tax, security, or legal and regulatory compliance.

+ +

6. Processing time

+

We aim to complete verified deletion requests within 7 business days, although certain cases may require additional time if further verification or compliance review is necessary.

+ +

7. Contact

+

If you have questions about account deletion or personal-data handling, contact chekrindo@gmail.com or call +85247410298.

+ + diff --git a/h5/laluprivacy.html b/h5/laluprivacy.html new file mode 100644 index 0000000..da64470 --- /dev/null +++ b/h5/laluprivacy.html @@ -0,0 +1,41 @@ + + + + + + Privacy Agreement | Lalu Voice Party + + + + +

Privacy Agreement for Lalu Voice Party

+

We at Lalu Voice Party value your privacy and are committed to protecting your personal information. This privacy policy explains how we collect, use, and protect your information. By using our app, you agree to the practices described in this privacy policy. Please read this policy carefully.

+ +

1. Information Collection

+

We may collect the following information:

+

Personal Information: The information you provide to us during the use of the app, including your device number, mobile phone model, version number, and other related data.

+

Use Data: Information about how you use the app, including device information and any other data generated during your interactions with the app.

+ +

2. Use of Information

+

We will use the personal information we collect for the following purposes:

+

To offer you a personalized experience and provide services tailored to your needs.

+

To understand how you use our app and make improvements based on your feedback.

+

To send you updates, offers, and notifications regarding the app.

+

We will not sell, rent, or share your personal information with third parties, unless we have your explicit consent or are required by law to do so.

+ +

3. Information Protection

+

We take the security of your personal information seriously. We employ various security technologies and procedures to protect your data from loss, misuse, unauthorized access, or disclosure. However, please be aware that no security system is completely infallible, and we cannot fully guarantee the absolute security of your information over the internet.

+ +

4. Third-Party Links

+

Our app may contain links to third-party websites or services. We are not responsible for the privacy practices or content of these external websites. Please review their privacy policies before engaging with any third-party sites.

+ +

5. Updates to the Privacy Agreement

+

We may update this Privacy Policy from time to time to reflect changes in our business practices or legal requirements. The updated policy will be posted on our website, and we will notify you of any changes before they take effect. Please review this privacy policy periodically to stay informed about any updates.

+

Last updated: April 16th, 2026

+ +

6. Contact Information

+

If you have any questions or concerns regarding our Privacy Policy or the protection of your personal information, please feel free to contact us at:

+

Email: chekrindo@gmail.com

+

We are committed to addressing your concerns in a timely and effective manner.

+ +