| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854 |
- // 管理后台API处理
- const { getDatabase } = require('./sql');
- const fs = require('fs');
- const path = require('path');
- const { promisify } = require('util');
- const { formidable } = require('formidable');
- const RECHARGE_FILE = path.join(__dirname, 'recharge.json');
- const PRODUCT_PRICING_FILE = path.join(__dirname, 'product-pricing.json');
- const mkdir = promisify(fs.mkdir);
- const access = promisify(fs.access);
- const readdir = promisify(fs.readdir);
- const stat = promisify(fs.stat);
- const unlink = promisify(fs.unlink);
- const rmdir = promisify(fs.rmdir);
- // 商店资源根目录(使用 market_data 目录)
- const STORE_DIR = path.join(__dirname, 'market_data');
- // 确保目录存在
- async function ensureDir(dirPath) {
- try {
- await access(dirPath);
- } catch (error) {
- await mkdir(dirPath, { recursive: true });
- }
- }
- // 递归删除目录
- async function deleteDirectory(dirPath) {
- try {
- const files = await readdir(dirPath);
- for (const file of files) {
- const filePath = path.join(dirPath, file);
- const stats = await stat(filePath);
- if (stats.isDirectory()) {
- await deleteDirectory(filePath);
- } else {
- await unlink(filePath);
- }
- }
- await rmdir(dirPath);
- } catch (error) {
- console.error('[Admin] 删除目录失败:', error);
- throw error;
- }
- }
- // 获取所有用户列表
- async function handleGetAllUsers(req, res) {
- try {
- console.log('[Admin] 收到获取用户列表请求');
- const db = await getDatabase();
- const users = db.getAllUsers();
-
- console.log('[Admin] 从数据库获取到用户数量:', users ? users.length : 0);
- console.log('[Admin] 用户数据:', users);
-
- res.writeHead(200, {
- 'Content-Type': 'application/json; charset=utf-8',
- 'Access-Control-Allow-Origin': '*' // 允许跨域访问
- });
- res.end(JSON.stringify({
- success: true,
- users: users || []
- }));
- } catch (error) {
- console.error('[Admin] 获取用户列表失败:', error);
- res.writeHead(500, {
- 'Content-Type': 'application/json; charset=utf-8',
- 'Access-Control-Allow-Origin': '*'
- });
- res.end(JSON.stringify({
- success: false,
- message: '获取用户列表失败: ' + error.message
- }));
- }
- }
- // 更新用户信息(管理员)
- async function handleAdminUpdateUser(req, res) {
- let body = '';
-
- req.on('data', chunk => {
- body += chunk.toString();
- });
-
- req.on('end', async () => {
- try {
- const data = JSON.parse(body);
- const { id, username, phone, points } = data;
-
- if (!id) {
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '缺少用户ID参数'
- }));
- return;
- }
-
- const db = await getDatabase();
- const user = db.findUserById(id);
-
- if (!user) {
- res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '用户不存在'
- }));
- return;
- }
-
- // 构建更新对象
- const updates = {};
- if (username !== undefined) {
- updates.username = username;
- }
- if (phone !== undefined) {
- updates.phone = phone;
- }
-
- // 更新用户信息
- if (Object.keys(updates).length > 0) {
- db.updateUser(id, updates);
- }
-
- // 更新点数(如果提供)
- if (points !== undefined && points !== null) {
- const currentPoints = db.getUserPoints(user.username);
- const diff = points - currentPoints;
- if (diff > 0) {
- db.addPoints(user.username, diff);
- } else if (diff < 0) {
- db.deductPoints(user.username, Math.abs(diff));
- }
- }
-
- res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: true,
- message: '更新成功'
- }));
- } catch (error) {
- console.error('[Admin] 更新用户失败:', error);
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '更新失败: ' + error.message
- }));
- }
- });
- }
- // 上传商店素材
- async function handleAdminUploadStore(req, res) {
- console.log('[Admin] ========== 收到上传请求 ==========');
- console.log('[Admin] 请求方法:', req.method);
- console.log('[Admin] 请求URL:', req.url);
- console.log('[Admin] Content-Type:', req.headers['content-type']);
-
- const form = formidable({
- uploadDir: path.join(__dirname, 'temp'),
- keepExtensions: true,
- multiples: true
- });
-
- form.parse(req, async (err, fields, files) => {
- if (err) {
- console.error('[Admin] ❌ 解析上传文件失败:', err);
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '上传失败: ' + err.message
- }));
- return;
- }
-
- console.log('[Admin] ✅ 文件解析成功');
- console.log('[Admin] fields:', JSON.stringify(fields, null, 2));
- console.log('[Admin] files keys:', Object.keys(files));
-
- try {
- const category = Array.isArray(fields.category) ? fields.category[0] : fields.category;
- const name = Array.isArray(fields.name) ? fields.name[0] : fields.name;
- const price = parseInt(Array.isArray(fields.price) ? fields.price[0] : fields.price) || 0;
-
- console.log('[Admin] 📦 上传参数:');
- console.log('[Admin] - 分类(category):', category);
- console.log('[Admin] - 文件夹名(name):', name);
- console.log('[Admin] - 价格(price):', price);
-
- if (!category || !name) {
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '缺少分类或名称参数'
- }));
- return;
- }
-
- // 获取上传的文件
- let fileList = [];
- if (files.files) {
- fileList = Array.isArray(files.files) ? files.files : [files.files];
- }
-
- console.log('[Admin] 📁 收到文件数量:', fileList.length);
-
- if (fileList.length === 0) {
- console.log('[Admin] ❌ 文件列表为空');
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '请选择要上传的文件'
- }));
- return;
- }
-
- // 创建目标目录
- const targetDir = path.join(STORE_DIR, category, name);
- console.log('[Admin] 📂 目标目录:', targetDir);
- console.log('[Admin] 📂 STORE_DIR:', STORE_DIR);
- await ensureDir(targetDir);
- console.log('[Admin] ✅ 目标目录已创建/确认存在');
-
- // 移动文件到目标目录,保持目录结构
- console.log('[Admin] 🔄 开始处理', fileList.length, '个文件...');
- let processedCount = 0;
- let skippedCount = 0;
-
- for (let i = 0; i < fileList.length; i++) {
- const file = fileList[i];
- const fileObj = Array.isArray(file) ? file[0] : file;
-
- if (!fileObj || !fileObj.filepath) {
- console.log(`[Admin] ⚠️ [${i+1}/${fileList.length}] 跳过无效文件对象`);
- skippedCount++;
- continue;
- }
-
- // 获取原始文件名(包含相对路径)
- const originalName = fileObj.originalFilename || path.basename(fileObj.filepath);
- console.log(`[Admin] 📄 [${i+1}/${fileList.length}] 处理文件:`, originalName);
-
- // 处理路径分隔符(统一使用 /)
- const normalizedName = originalName.replace(/\\/g, '/');
-
- // 如果 originalName 包含文件夹名(如 player_0001/image.png),需要去掉文件夹名前缀
- // 因为 targetDir 已经是 market_data/category/name 了
- let relativePath = normalizedName;
-
- // 如果路径以文件夹名开头(如 player_0001/image.png),去掉文件夹名
- if (normalizedName.startsWith(name + '/')) {
- relativePath = normalizedName.substring(name.length + 1);
- console.log(`[Admin] └─ 去掉文件夹前缀 "${name}/",相对路径:`, relativePath);
- }
-
- const targetPath = path.join(targetDir, relativePath);
- console.log(`[Admin] └─ 目标路径:`, targetPath);
-
- // 确保目标目录存在
- await ensureDir(path.dirname(targetPath));
-
- // 复制文件
- await fs.promises.copyFile(fileObj.filepath, targetPath);
- console.log(`[Admin] └─ ✅ 文件已复制成功`);
- processedCount++;
-
- // 删除临时文件
- try {
- await fs.promises.unlink(fileObj.filepath);
- } catch (err) {
- console.warn(`[Admin] └─ ⚠️ 删除临时文件失败:`, err.message);
- }
- }
-
- console.log('[Admin] ========== 上传处理完成 ==========');
- console.log('[Admin] ✅ 成功处理:', processedCount, '个文件');
- if (skippedCount > 0) {
- console.log('[Admin] ⚠️ 跳过:', skippedCount, '个无效文件');
- }
- console.log('[Admin] 📂 最终保存位置:', targetDir);
-
- // 更新商店资源配置(如果需要)
- // 这里可以添加更新商店配置文件的逻辑
-
- res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: true,
- message: '上传成功',
- path: `${category}/${name}`,
- fileCount: processedCount
- }));
- } catch (error) {
- console.error('[Admin] ❌ ========== 上传失败 ==========');
- console.error('[Admin] 错误信息:', error.message);
- console.error('[Admin] 错误堆栈:', error.stack);
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '上传失败: ' + error.message
- }));
- }
- });
- }
- // 删除商店素材
- async function handleAdminDeleteStore(req, res) {
- let body = '';
-
- req.on('data', chunk => {
- body += chunk.toString();
- });
-
- req.on('end', async () => {
- try {
- const data = JSON.parse(body);
- const { resourcePath } = data;
-
- console.log('[Admin] 删除请求 - resourcePath:', resourcePath);
- console.log('[Admin] STORE_DIR:', STORE_DIR);
-
- if (!resourcePath) {
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '缺少资源路径参数'
- }));
- return;
- }
-
- // 构建完整路径
- const fullPath = path.join(STORE_DIR, resourcePath);
- console.log('[Admin] 完整路径:', fullPath);
-
- // 安全检查
- const normalizedPath = path.normalize(fullPath);
- const normalizedRoot = path.normalize(STORE_DIR);
- console.log('[Admin] normalizedPath:', normalizedPath);
- console.log('[Admin] normalizedRoot:', normalizedRoot);
-
- if (!normalizedPath.startsWith(normalizedRoot)) {
- console.log('[Admin] 安全检查失败');
- res.writeHead(403, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '访问被拒绝'
- }));
- return;
- }
-
- // 检查路径是否存在
- try {
- const stats = await stat(fullPath);
- console.log('[Admin] 路径存在,是目录:', stats.isDirectory());
-
- if (stats.isDirectory()) {
- await deleteDirectory(fullPath);
- } else {
- await unlink(fullPath);
- }
-
- console.log('[Admin] 删除成功');
- res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: true,
- message: '删除成功'
- }));
- } catch (error) {
- console.log('[Admin] stat 错误:', error.code, error.message);
- if (error.code === 'ENOENT') {
- res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '资源不存在'
- }));
- } else {
- throw error;
- }
- }
- } catch (error) {
- console.error('[Admin] 删除素材失败:', error);
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '删除失败: ' + error.message
- }));
- }
- });
- }
- // 创建分类文件夹
- async function handleAdminCreateFolder(req, res) {
- let body = '';
-
- req.on('data', chunk => {
- body += chunk.toString();
- });
-
- req.on('end', async () => {
- try {
- const data = JSON.parse(body);
- const { name } = data;
-
- if (!name || !name.trim()) {
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '缺少文件夹名称参数'
- }));
- return;
- }
- const folderName = name.trim();
- // 验证文件夹名称
- if (/[\\/:*?"<>|]/.test(folderName)) {
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '文件夹名称包含非法字符'
- }));
- return;
- }
- // 创建分类文件夹(在 STORE_DIR 根目录下)
- const targetDir = path.join(STORE_DIR, folderName);
-
- // 检查文件夹是否已存在
- try {
- await access(targetDir);
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '文件夹已存在'
- }));
- return;
- } catch (error) {
- if (error.code !== 'ENOENT') {
- throw error;
- }
- // 文件夹不存在,创建它
- await mkdir(targetDir, { recursive: true });
- }
- res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: true,
- message: '创建文件夹成功',
- path: folderName
- }));
- } catch (error) {
- console.error('[Admin] 创建文件夹失败:', error);
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '创建文件夹失败: ' + error.message
- }));
- }
- });
- }
- // 重命名商店素材
- async function handleAdminRenameStore(req, res) {
- let body = '';
-
- req.on('data', chunk => {
- body += chunk.toString();
- });
-
- req.on('end', async () => {
- try {
- const data = JSON.parse(body);
- const { resourcePath, newName } = data;
-
- if (!resourcePath || !newName) {
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '缺少资源路径或新名称参数'
- }));
- return;
- }
- // 验证新名称
- if (/[\\/:*?"<>|]/.test(newName)) {
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '名称包含非法字符'
- }));
- return;
- }
- // 构建完整路径
- const fullPath = path.join(STORE_DIR, resourcePath);
-
- // 安全检查
- const normalizedPath = path.normalize(fullPath);
- const normalizedRoot = path.normalize(STORE_DIR);
- if (!normalizedPath.startsWith(normalizedRoot)) {
- res.writeHead(403, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '访问被拒绝'
- }));
- return;
- }
- // 检查路径是否存在
- try {
- await access(fullPath);
- } catch (error) {
- if (error.code === 'ENOENT') {
- res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '资源不存在'
- }));
- return;
- } else {
- throw error;
- }
- }
- // 构建新路径
- const parentDir = path.dirname(fullPath);
- const newFullPath = path.join(parentDir, newName);
- // 检查新名称是否已存在
- try {
- await access(newFullPath);
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '新名称已存在'
- }));
- return;
- } catch (error) {
- if (error.code !== 'ENOENT') {
- throw error;
- }
- }
- // 重命名文件或文件夹
- await fs.promises.rename(fullPath, newFullPath);
- const newPathKey = path.relative(STORE_DIR, newFullPath).replace(/\\/g, '/');
- const oldPathKey = resourcePath;
- // 检查是否是分类文件夹(在根目录下)
- const stats = await stat(newFullPath);
- const isCategoryFolder = stats.isDirectory() && !resourcePath.includes('/');
- // 更新价格文件中的路径(如果存在)
- try {
- const pricesFilePath = path.join(STORE_DIR, 'prices.json');
- const pricesData = await fs.promises.readFile(pricesFilePath, 'utf-8');
- const prices = JSON.parse(pricesData);
-
- // 更新所有相关的价格路径
- // 如果是分类文件夹,需要更新所有子资源的价格路径
- if (isCategoryFolder) {
- const updatedPrices = {};
- for (const [key, value] of Object.entries(prices)) {
- if (key.startsWith(oldPathKey + '/')) {
- // 更新子资源的路径
- const newKey = key.replace(oldPathKey + '/', newPathKey + '/');
- updatedPrices[newKey] = value;
- } else if (key === oldPathKey) {
- // 更新分类文件夹本身的价格(如果有)
- updatedPrices[newPathKey] = value;
- } else {
- // 保留其他路径
- updatedPrices[key] = value;
- }
- }
- await fs.promises.writeFile(pricesFilePath, JSON.stringify(updatedPrices, null, 2), 'utf-8');
- } else {
- // 只更新当前资源的价格路径
- if (prices[oldPathKey] !== undefined) {
- prices[newPathKey] = prices[oldPathKey];
- delete prices[oldPathKey];
- await fs.promises.writeFile(pricesFilePath, JSON.stringify(prices, null, 2), 'utf-8');
- }
- }
- } catch (error) {
- // 价格文件不存在或更新失败,不影响重命名操作
- console.warn('[Admin] 更新价格文件失败:', error);
- }
- res.writeHead(200, {
- 'Content-Type': 'application/json; charset=utf-8',
- 'Access-Control-Allow-Origin': '*'
- });
- res.end(JSON.stringify({
- success: true,
- message: '重命名成功',
- newPath: newPathKey,
- isCategoryFolder: isCategoryFolder
- }));
- } catch (error) {
- console.error('[Admin] 重命名素材失败:', error);
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '重命名失败: ' + error.message
- }));
- }
- });
- }
- // 更新资源价格
- async function handleAdminUpdatePrice(req, res) {
- let body = '';
-
- req.on('data', chunk => {
- body += chunk.toString();
- });
-
- req.on('end', async () => {
- try {
- const data = JSON.parse(body);
- const { resourcePath, price } = data;
-
- if (!resourcePath || price === undefined) {
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '缺少资源路径或价格参数'
- }));
- return;
- }
-
- // 价格数据存储在JSON文件中
- const pricesFilePath = path.join(STORE_DIR, 'prices.json');
- let prices = {};
-
- try {
- const pricesData = await fs.promises.readFile(pricesFilePath, 'utf-8');
- prices = JSON.parse(pricesData);
- } catch (error) {
- // 文件不存在,创建新的
- prices = {};
- }
-
- // 更新价格
- prices[resourcePath] = price;
-
- // 保存到文件
- await fs.promises.writeFile(pricesFilePath, JSON.stringify(prices, null, 2), 'utf-8');
-
- // 通知 StoreManager 清除价格缓存(通过事件或直接调用)
- // 注意:这里 StoreManager 的缓存会在下次请求时自动清除
-
- res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: true,
- message: '价格更新成功'
- }));
- } catch (error) {
- console.error('[Admin] 更新价格失败:', error);
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '更新失败: ' + error.message
- }));
- }
- });
- }
- // 货币设置文件路径
- const CURRENCY_SETTINGS_FILE = path.join(__dirname, 'currency-settings.json');
- // 获取货币/充值套餐设置
- async function handleGetCurrencySettings(req, res) {
- try {
- let packages = [
- { points: 100, bonus: 20, price: 5 },
- { points: 1000, bonus: 200, price: 50 },
- { points: 10000, bonus: 800, price: 500 }
- ];
-
- try {
- const data = await fs.promises.readFile(CURRENCY_SETTINGS_FILE, 'utf-8');
- const parsed = JSON.parse(data);
- packages = parsed.packages || packages;
- } catch (err) {
- // 文件不存在,使用默认值
- }
-
- res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: true,
- packages
- }));
- } catch (error) {
- console.error('[Admin] 获取充值套餐失败:', error);
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '获取设置失败: ' + error.message
- }));
- }
- }
- // 保存货币/充值套餐设置
- async function handleSaveCurrencySettings(req, res) {
- let body = '';
- req.on('data', chunk => { body += chunk.toString(); });
- req.on('end', async () => {
- try {
- const data = JSON.parse(body);
- const { packages } = data;
-
- await fs.promises.writeFile(CURRENCY_SETTINGS_FILE, JSON.stringify({ packages }, null, 2), 'utf-8');
-
- res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: true,
- message: '充值套餐保存成功'
- }));
- } catch (error) {
- console.error('[Admin] 保存充值套餐失败:', error);
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '保存失败: ' + error.message
- }));
- }
- });
- }
- // 获取商品定价设置
- async function handleGetProductPricingSettings(req, res) {
- try {
- let products = [
- { id: 'vip-matting', name: 'VIP抠图', desc: '使用VIP服务进行图片抠图', price: 0 },
- { id: 'ai-generate', name: 'AI生图', desc: '使用AI生成图片', price: 0 }
- ];
-
- try {
- const data = await fs.promises.readFile(PRODUCT_PRICING_FILE, 'utf-8');
- const parsed = JSON.parse(data);
- if (parsed.products && Array.isArray(parsed.products)) {
- // 合并服务器保存的价格
- parsed.products.forEach(serverProduct => {
- const localProduct = products.find(p => p.id === serverProduct.id);
- if (localProduct) {
- localProduct.price = serverProduct.price || 0;
- }
- });
- }
- } catch (err) {
- // 文件不存在,使用默认值
- }
-
- res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: true,
- products
- }));
- } catch (error) {
- console.error('[Admin] 获取商品定价失败:', error);
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '获取设置失败: ' + error.message
- }));
- }
- }
- // 保存商品定价设置
- async function handleSaveProductPricingSettings(req, res) {
- let body = '';
- req.on('data', chunk => { body += chunk.toString(); });
- req.on('end', async () => {
- try {
- const data = JSON.parse(body);
- const { productId, price } = data;
-
- if (!productId || price === undefined) {
- res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '缺少必要参数'
- }));
- return;
- }
-
- // 读取现有设置
- let products = [
- { id: 'vip-matting', name: 'VIP抠图', desc: '使用VIP服务进行图片抠图', price: 0 },
- { id: 'ai-generate', name: 'AI生图', desc: '使用AI生成图片', price: 0 }
- ];
-
- try {
- const fileData = await fs.promises.readFile(PRODUCT_PRICING_FILE, 'utf-8');
- const parsed = JSON.parse(fileData);
- if (parsed.products && Array.isArray(parsed.products)) {
- products = parsed.products;
- }
- } catch (err) {
- // 文件不存在,使用默认值
- }
-
- // 更新指定商品的价格
- const product = products.find(p => p.id === productId);
- if (product) {
- product.price = parseFloat(price) || 0;
- } else {
- res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '商品不存在'
- }));
- return;
- }
-
- await fs.promises.writeFile(PRODUCT_PRICING_FILE, JSON.stringify({ products }, null, 2), 'utf-8');
-
- res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: true,
- message: '价格保存成功'
- }));
- } catch (error) {
- console.error('[Admin] 保存商品定价失败:', error);
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
- res.end(JSON.stringify({
- success: false,
- message: '保存失败: ' + error.message
- }));
- }
- });
- }
- module.exports = {
- handleGetAllUsers,
- handleAdminUpdateUser,
- handleAdminUploadStore,
- handleAdminDeleteStore,
- handleAdminCreateFolder,
- handleAdminRenameStore,
- handleAdminUpdatePrice,
- handleGetCurrencySettings,
- handleSaveCurrencySettings,
- handleGetProductPricingSettings,
- handleSaveProductPricingSettings
- };
|