|
|
@@ -0,0 +1,268 @@
|
|
|
+const db = require("../plugin/DataBase/db");
|
|
|
+
|
|
|
+function normalizeClientType(value) {
|
|
|
+ return String(value || "").trim().toLowerCase();
|
|
|
+}
|
|
|
+
|
|
|
+function parseVersionParts(version) {
|
|
|
+ return String(version || "")
|
|
|
+ .trim()
|
|
|
+ .replace(/^[vV]/, "")
|
|
|
+ .split(/[.+_-]/)
|
|
|
+ .map(part => {
|
|
|
+ const n = Number.parseInt(part, 10);
|
|
|
+ return Number.isFinite(n) ? n : 0;
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function compareVersionName(a, b) {
|
|
|
+ const left = parseVersionParts(a);
|
|
|
+ const right = parseVersionParts(b);
|
|
|
+ const len = Math.max(left.length, right.length);
|
|
|
+ for (let i = 0; i < len; i++) {
|
|
|
+ const diff = (left[i] || 0) - (right[i] || 0);
|
|
|
+ if (diff !== 0) return diff > 0 ? 1 : -1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+function isVersionNewer(remote, local) {
|
|
|
+ const remoteCode = Number(remote.version_code);
|
|
|
+ const localCode = Number(local.version_code);
|
|
|
+ if (Number.isFinite(remoteCode) && remoteCode > 0 && Number.isFinite(localCode) && localCode > 0)
|
|
|
+ return remoteCode > localCode;
|
|
|
+ return compareVersionName(remote.version_name, local.version_name) > 0;
|
|
|
+}
|
|
|
+
|
|
|
+function parseOptionalNumber(value) {
|
|
|
+ const n = Number(value);
|
|
|
+ return Number.isFinite(n) && n > 0 ? n : 0;
|
|
|
+}
|
|
|
+
|
|
|
+function compareClientVersion(left, right) {
|
|
|
+ const leftCode = parseOptionalNumber(left.version_code);
|
|
|
+ const rightCode = parseOptionalNumber(right.version_code);
|
|
|
+ if (leftCode > 0 && rightCode > 0) {
|
|
|
+ if (leftCode === rightCode) return 0;
|
|
|
+ return leftCode > rightCode ? 1 : -1;
|
|
|
+ }
|
|
|
+ return compareVersionName(left.version_name, right.version_name);
|
|
|
+}
|
|
|
+
|
|
|
+function hasRangeBoundary(boundary) {
|
|
|
+ return Boolean(boundary.version_name) || parseOptionalNumber(boundary.version_code) > 0;
|
|
|
+}
|
|
|
+
|
|
|
+function versionMatchesRange(row, localVersion = {}) {
|
|
|
+ const local = {
|
|
|
+ version_name: localVersion.version_name,
|
|
|
+ version_code: localVersion.version_code
|
|
|
+ };
|
|
|
+ const minVersion = {
|
|
|
+ version_name: row.min_current_version,
|
|
|
+ version_code: row.min_current_version_code
|
|
|
+ };
|
|
|
+ const maxVersion = {
|
|
|
+ version_name: row.max_current_version,
|
|
|
+ version_code: row.max_current_version_code
|
|
|
+ };
|
|
|
+
|
|
|
+ if (hasRangeBoundary(minVersion) && compareClientVersion(local, minVersion) < 0)
|
|
|
+ return false;
|
|
|
+ if (hasRangeBoundary(maxVersion) && compareClientVersion(local, maxVersion) > 0)
|
|
|
+ return false;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+function toUniAppPayload(row) {
|
|
|
+ return {
|
|
|
+ describe: row.release_notes_html || row.release_notes || "",
|
|
|
+ edition_url: row.download_url,
|
|
|
+ edition_force: Number(row.force_update) || 0,
|
|
|
+ package_type: Number(row.package_type) || 0,
|
|
|
+ edition_issue: Number(row.is_active) === 0 ? 0 : 1,
|
|
|
+ edition_number: Number(row.version_code) || 0,
|
|
|
+ edition_name: row.version_name,
|
|
|
+ edition_silence: Number(row.silent_update) || 0,
|
|
|
+ log: row.release_notes || ""
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+function toPublicPayload(row, localVersion = {}) {
|
|
|
+ if (!row) {
|
|
|
+ return {
|
|
|
+ has_update: false,
|
|
|
+ update_available: false
|
|
|
+ };
|
|
|
+ }
|
|
|
+ const remote = {
|
|
|
+ version_name: row.version_name,
|
|
|
+ version_code: Number(row.version_code) || 0
|
|
|
+ };
|
|
|
+ const local = {
|
|
|
+ version_name: localVersion.version_name,
|
|
|
+ version_code: localVersion.version_code
|
|
|
+ };
|
|
|
+ const hasUpdate = isVersionNewer(remote, local);
|
|
|
+ const payload = {
|
|
|
+ has_update: hasUpdate,
|
|
|
+ update_available: hasUpdate,
|
|
|
+ client_type: row.client_type,
|
|
|
+ title: row.title || "",
|
|
|
+ version: row.version_name,
|
|
|
+ version_name: row.version_name,
|
|
|
+ version_code: Number(row.version_code) || 0,
|
|
|
+ download_url: row.download_url,
|
|
|
+ sha256: row.sha256 || "",
|
|
|
+ file_size: Number(row.file_size) || 0,
|
|
|
+ package_type: Number(row.package_type) || 0,
|
|
|
+ force_update: Number(row.force_update) || 0,
|
|
|
+ silent_update: Number(row.silent_update) || 0,
|
|
|
+ release_notes: row.release_notes || "",
|
|
|
+ release_notes_html: row.release_notes_html || "",
|
|
|
+ min_supported_version: row.min_supported_version || "",
|
|
|
+ min_current_version: row.min_current_version || "",
|
|
|
+ min_current_version_code: Number(row.min_current_version_code) || 0,
|
|
|
+ max_current_version: row.max_current_version || "",
|
|
|
+ max_current_version_code: Number(row.max_current_version_code) || 0
|
|
|
+ };
|
|
|
+ if (normalizeClientType(row.client_type) === "android") {
|
|
|
+ Object.assign(payload, toUniAppPayload(row), {
|
|
|
+ uniapp: toUniAppPayload(row)
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return payload;
|
|
|
+}
|
|
|
+
|
|
|
+async function getLatestVersion(clientType) {
|
|
|
+ return await getMatchedVersion(clientType, {});
|
|
|
+}
|
|
|
+
|
|
|
+async function getMatchedVersion(clientType, localVersion = {}) {
|
|
|
+ const normalized = normalizeClientType(clientType);
|
|
|
+ const rows = await db.query(`
|
|
|
+ SELECT id, client_type, version_name, version_code, title, download_url,
|
|
|
+ sha256, file_size, package_type, force_update, silent_update,
|
|
|
+ is_active, release_notes, release_notes_html, min_supported_version,
|
|
|
+ min_current_version, min_current_version_code,
|
|
|
+ max_current_version, max_current_version_code,
|
|
|
+ sort_order, created_at, updated_at
|
|
|
+ FROM download_version
|
|
|
+ WHERE client_type = ? AND is_active = 1
|
|
|
+ ORDER BY version_code DESC, sort_order ASC, id DESC
|
|
|
+ `, [normalized]);
|
|
|
+ if (!rows || rows.length === 0) return null;
|
|
|
+
|
|
|
+ const updateRows = rows.filter(row => isVersionNewer(row, localVersion));
|
|
|
+ return updateRows.find(row => versionMatchesRange(row, localVersion)) || updateRows[0] || null;
|
|
|
+}
|
|
|
+
|
|
|
+async function listVersions(clientType = "") {
|
|
|
+ const normalized = normalizeClientType(clientType);
|
|
|
+ if (normalized) {
|
|
|
+ return await db.query(`
|
|
|
+ SELECT id, client_type, version_name, version_code, title, download_url,
|
|
|
+ sha256, file_size, package_type, force_update, silent_update,
|
|
|
+ is_active, release_notes, release_notes_html, min_supported_version,
|
|
|
+ min_current_version, min_current_version_code,
|
|
|
+ max_current_version, max_current_version_code,
|
|
|
+ sort_order, created_at, updated_at
|
|
|
+ FROM download_version
|
|
|
+ WHERE client_type = ?
|
|
|
+ ORDER BY client_type ASC, version_code DESC, sort_order ASC, id DESC
|
|
|
+ `, [normalized]);
|
|
|
+ }
|
|
|
+ return await db.query(`
|
|
|
+ SELECT id, client_type, version_name, version_code, title, download_url,
|
|
|
+ sha256, file_size, package_type, force_update, silent_update,
|
|
|
+ is_active, release_notes, release_notes_html, min_supported_version,
|
|
|
+ min_current_version, min_current_version_code,
|
|
|
+ max_current_version, max_current_version_code,
|
|
|
+ sort_order, created_at, updated_at
|
|
|
+ FROM download_version
|
|
|
+ ORDER BY client_type ASC, version_code DESC, sort_order ASC, id DESC
|
|
|
+ `);
|
|
|
+}
|
|
|
+
|
|
|
+async function saveVersion(payload) {
|
|
|
+ const now = Date.now();
|
|
|
+ const data = {
|
|
|
+ client_type: normalizeClientType(payload.client_type),
|
|
|
+ version_name: String(payload.version_name || payload.version || "").trim(),
|
|
|
+ version_code: Number(payload.version_code) || 0,
|
|
|
+ title: String(payload.title || "").trim(),
|
|
|
+ download_url: String(payload.download_url || "").trim(),
|
|
|
+ sha256: String(payload.sha256 || "").trim().toLowerCase(),
|
|
|
+ file_size: Number(payload.file_size) || 0,
|
|
|
+ package_type: Number(payload.package_type) || 0,
|
|
|
+ force_update: Number(payload.force_update) === 1 ? 1 : 0,
|
|
|
+ silent_update: Number(payload.silent_update) === 1 ? 1 : 0,
|
|
|
+ is_active: Number(payload.is_active) === 0 ? 0 : 1,
|
|
|
+ release_notes: payload.release_notes || "",
|
|
|
+ release_notes_html: payload.release_notes_html || "",
|
|
|
+ min_supported_version: String(payload.min_supported_version || "").trim(),
|
|
|
+ min_current_version: String(payload.min_current_version || "").trim(),
|
|
|
+ min_current_version_code: Number(payload.min_current_version_code) || 0,
|
|
|
+ max_current_version: String(payload.max_current_version || "").trim(),
|
|
|
+ max_current_version_code: Number(payload.max_current_version_code) || 0,
|
|
|
+ sort_order: Number(payload.sort_order) || 0
|
|
|
+ };
|
|
|
+
|
|
|
+ if (payload.id) {
|
|
|
+ const result = await db.query(`
|
|
|
+ UPDATE download_version SET
|
|
|
+ client_type = ?, version_name = ?, version_code = ?, title = ?,
|
|
|
+ download_url = ?, sha256 = ?, file_size = ?, package_type = ?,
|
|
|
+ force_update = ?, silent_update = ?, is_active = ?,
|
|
|
+ release_notes = ?, release_notes_html = ?, min_supported_version = ?,
|
|
|
+ min_current_version = ?, min_current_version_code = ?,
|
|
|
+ max_current_version = ?, max_current_version_code = ?,
|
|
|
+ sort_order = ?, updated_at = ?
|
|
|
+ WHERE id = ?
|
|
|
+ `, [
|
|
|
+ data.client_type, data.version_name, data.version_code, data.title,
|
|
|
+ data.download_url, data.sha256, data.file_size, data.package_type,
|
|
|
+ data.force_update, data.silent_update, data.is_active,
|
|
|
+ data.release_notes, data.release_notes_html, data.min_supported_version,
|
|
|
+ data.min_current_version, data.min_current_version_code,
|
|
|
+ data.max_current_version, data.max_current_version_code,
|
|
|
+ data.sort_order, now, payload.id
|
|
|
+ ]);
|
|
|
+ return { id: payload.id, result };
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = await db.query(`
|
|
|
+ INSERT INTO download_version
|
|
|
+ (client_type, version_name, version_code, title, download_url,
|
|
|
+ sha256, file_size, package_type, force_update, silent_update,
|
|
|
+ is_active, release_notes, release_notes_html, min_supported_version,
|
|
|
+ min_current_version, min_current_version_code,
|
|
|
+ max_current_version, max_current_version_code,
|
|
|
+ sort_order, created_at, updated_at)
|
|
|
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
+ `, [
|
|
|
+ data.client_type, data.version_name, data.version_code, data.title,
|
|
|
+ data.download_url, data.sha256, data.file_size, data.package_type,
|
|
|
+ data.force_update, data.silent_update, data.is_active,
|
|
|
+ data.release_notes, data.release_notes_html, data.min_supported_version,
|
|
|
+ data.min_current_version, data.min_current_version_code,
|
|
|
+ data.max_current_version, data.max_current_version_code,
|
|
|
+ data.sort_order, now, now
|
|
|
+ ]);
|
|
|
+ return { id: result && result.insertId, result };
|
|
|
+}
|
|
|
+
|
|
|
+async function deleteVersion(id) {
|
|
|
+ return await db.query("DELETE FROM download_version WHERE id = ?", [id]);
|
|
|
+}
|
|
|
+
|
|
|
+module.exports = {
|
|
|
+ getLatestVersion,
|
|
|
+ getMatchedVersion,
|
|
|
+ listVersions,
|
|
|
+ saveVersion,
|
|
|
+ deleteVersion,
|
|
|
+ toPublicPayload,
|
|
|
+ toUniAppPayload,
|
|
|
+ normalizeClientType
|
|
|
+};
|