Files
jurong_circle_shopping_black/routes/specifications.js

654 lines
22 KiB
JavaScript
Raw Normal View History

2025-09-24 10:02:03 +08:00
const express = require('express');
const router = express.Router();
const {getDB} = require('../database');
const {auth, adminAuth} = require('../middleware/auth');
const {SelectBuilder} = require('../config/dbv2')
const {db} = require("../server");
const sql = require("../config/config");
router.get('/names', auth, async (req, res) => {
try {
const {status = 'active'} = req.query;
const {id: created_id} = req.user;
let query = `SELECT *
FROM spec_names
WHERE created_id = ${created_id}`;
const params = [];
if (status) {
query += ' and status = ?';
params.push(status);
}
query += ' ORDER BY sort_order, id';
const [specNames] = await getDB().execute(query, params);
res.json({
success: true,
data: specNames
});
} catch (error) {
console.error('获取规格名称失败:', error);
res.status(500).json({success: false, message: '获取规格名称失败'});
2025-09-24 10:02:03 +08:00
}
});
2025-09-24 10:02:03 +08:00
router.post('/names', auth, adminAuth, async (req, res) => {
const db = getDB();
try {
const {name, display_name, sort_order = 0} = req.body;
const {id: created_id} = req.user;
if (!name || !display_name) {
return res.status(400).json({success: false, message: '规格名称和显示名称不能为空'});
}
let spec_name = new SelectBuilder().from()
const [result] = await getDB().execute(
'INSERT INTO spec_names (name, display_name, sort_order,created_id) VALUES (?, ?, ?,?)',
[name, display_name, sort_order,created_id]
);
res.status(201).json({
success: true,
message: '规格名称创建成功',
data: {id: result.insertId}
});
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
return res.status(400).json({success: false, message: '规格名称已存在'});
}
console.error('创建规格名称失败:', error);
res.status(500).json({success: false, message: '创建规格名称失败'});
2025-09-24 10:02:03 +08:00
}
});
2025-09-24 10:02:03 +08:00
router.delete('/names/:id', auth, adminAuth, async (req, res) => {
try {
const {id} = req.params;
const {id:user_id,role} = req.user
let specCountQuery = new SelectBuilder()
.from('spec_names')
.select('COUNT(*) as total')
.where('id=?',id)
if(role !== 'admin'){
specCountQuery.where('created_id=?',user_id)
}
let [spec] = await specCountQuery.execute(db)
if (spec.total === 0) {
return res.status(404).json({success: false, message: '规格名称不存在'});
}
// 检查该规格名称下是否还有规格值
const [specValues] = await getDB().execute(
'SELECT COUNT(*) as count FROM spec_values WHERE spec_name_id = ?',
[id]
);
if (specValues[0].count > 0) {
return res.status(400).json({success: false, message: '该规格名称下还有规格值,请先删除所有规格值'});
}
// 删除规格名称
await getDB().execute(
'DELETE FROM spec_names WHERE id = ?',
[id]
);
res.json({
success: true,
message: '规格名称删除成功'
});
} catch (error) {
console.error('删除规格名称失败:', error);
res.status(500).json({success: false, message: '删除规格名称失败'});
2025-09-24 10:02:03 +08:00
}
});
2025-09-24 10:02:03 +08:00
router.get('/values', async (req, res) => {
try {
const {spec_name_id, status = 'active'} = req.query;
let query = `
SELECT sv.*, sn.name as spec_name, sn.display_name as spec_display_name
FROM spec_values sv
LEFT JOIN spec_names sn ON sv.spec_name_id = sn.id
WHERE 1 = 1
`;
const params = [];
if (spec_name_id) {
query += ' AND sv.spec_name_id = ?';
params.push(spec_name_id);
}
if (status) {
query += ' AND sv.status = ?';
params.push(status);
}
query += ' ORDER BY sv.spec_name_id, sv.sort_order, sv.id';
const [specValues] = await getDB().execute(query, params);
res.json({
success: true,
data: specValues
});
} catch (error) {
console.error('获取规格值失败:', error);
res.status(500).json({success: false, message: '获取规格值失败'});
2025-09-24 10:02:03 +08:00
}
});
2025-09-24 10:02:03 +08:00
router.post('/values', auth, adminAuth, async (req, res) => {
try {
const {spec_name_id, value, display_value, color_code, image_url, sort_order = 0} = req.body;
if (!spec_name_id || !value || !display_value) {
return res.status(400).json({success: false, message: '规格名称ID、规格值和显示值不能为空'});
}
const [result] = await getDB().execute(
`INSERT INTO spec_values (spec_name_id, value, display_value, color_code, image_url, sort_order)
VALUES (?, ?, ?, ?, ?, ?)`,
[spec_name_id, value, display_value, color_code || null, image_url || null, sort_order]
);
res.status(201).json({
success: true,
message: '规格值创建成功',
data: {id: result.insertId}
});
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
return res.status(400).json({success: false, message: '该规格名称下的规格值已存在'});
}
console.error('创建规格值失败:', error);
res.status(500).json({success: false, message: '创建规格值失败'});
2025-09-24 10:02:03 +08:00
}
});
2025-09-24 10:02:03 +08:00
router.get('/combinations/:productId', async (req, res) => {
try {
const {productId} = req.params;
const {status = 'active'} = req.query;
// 获取商品的规格组合
let query = `
SELECT psc.*,
p.name as product_name,
p.price as base_price,
p.points_price as base_points_price,
p.rongdou_price as base_rongdou_price
FROM product_spec_combinations psc
LEFT JOIN products p ON psc.product_id = p.id
WHERE psc.product_id = ?
`;
const params = [productId];
if (status) {
query += ' AND psc.status = ?';
params.push(status);
2025-09-24 10:02:03 +08:00
}
query += ' ORDER BY psc.combination_key';
const [combinations] = await getDB().execute(query, params);
// 为每个组合获取详细的规格值信息
for (let combination of combinations) {
let specValueIds;
try {
// 处理不同的数据格式
if (!combination.spec_values) {
specValueIds = [];
} else if (typeof combination.spec_values === 'string') {
// 如果是字符串尝试JSON解析失败则按逗号分隔处理
try {
specValueIds = JSON.parse(combination.spec_values);
} catch {
// 按逗号分隔的字符串处理
specValueIds = combination.spec_values.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id));
}
} else if (Buffer.isBuffer(combination.spec_values)) {
// 如果是Buffer转换为字符串后处理
const strValue = combination.spec_values.toString();
try {
specValueIds = JSON.parse(strValue);
} catch {
specValueIds = strValue.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id));
}
} else {
// 其他情况,尝试直接使用
specValueIds = Array.isArray(combination.spec_values) ? combination.spec_values : [];
}
} catch (error) {
console.error('解析规格值失败:', combination.spec_values, error);
specValueIds = [];
}
if (specValueIds && specValueIds.length > 0) {
const placeholders = specValueIds.map(() => '?').join(',');
const [specDetails] = await getDB().execute(
`SELECT sv.*, sn.name as spec_name, sn.display_name as spec_display_name
FROM spec_values sv
LEFT JOIN spec_names sn ON sv.spec_name_id = sn.id
WHERE sv.id IN (${placeholders})
ORDER BY sn.sort_order, sv.sort_order`,
specValueIds
);
combination.spec_details = specDetails;
} else {
combination.spec_details = [];
}
// 计算实际价格
combination.actual_price = combination.base_price + (combination.price_adjustment || 0);
combination.actual_points_price = combination.base_points_price + (combination.points_adjustment || 0);
combination.actual_rongdou_price = combination.base_rongdou_price + (combination.rongdou_adjustment || 0);
combination.is_available = combination.stock > 0;
}
res.json({
success: true,
data: combinations
});
} catch (error) {
console.error('获取规格组合失败:', error);
res.status(500).json({success: false, message: '获取规格组合失败'});
2025-09-24 10:02:03 +08:00
}
});
router.get('/combinations/:id', async (req, res) => {
try {
const {id} = req.params;
// 获取规格组合详情
const [combinations] = await getDB().execute(
`SELECT psc.*,
p.name as product_name,
p.price as base_price,
p.points_price as base_points_price,
p.rongdou_price as base_rongdou_price
FROM product_spec_combinations psc
LEFT JOIN products p ON psc.product_id = p.id
WHERE psc.id = ?`,
[id]
);
if (combinations.length === 0) {
return res.status(404).json({success: false, message: '规格组合不存在'});
2025-09-24 10:02:03 +08:00
}
const combination = combinations[0];
// 解析规格值并获取详细信息
let specValueIds;
2025-09-24 10:02:03 +08:00
try {
if (!combination.spec_values) {
specValueIds = [];
} else if (typeof combination.spec_values === 'string') {
try {
specValueIds = JSON.parse(combination.spec_values);
} catch {
specValueIds = combination.spec_values.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id));
}
} else if (Buffer.isBuffer(combination.spec_values)) {
const strValue = combination.spec_values.toString();
try {
specValueIds = JSON.parse(strValue);
} catch {
specValueIds = strValue.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id));
}
} else {
specValueIds = Array.isArray(combination.spec_values) ? combination.spec_values : [];
}
} catch (error) {
console.error('解析规格值失败:', combination.spec_values, error);
specValueIds = [];
2025-09-24 10:02:03 +08:00
}
if (specValueIds && specValueIds.length > 0) {
const placeholders = specValueIds.map(() => '?').join(',');
const [specDetails] = await getDB().execute(
`SELECT sv.*, sn.name as spec_name, sn.display_name as spec_display_name
FROM spec_values sv
LEFT JOIN spec_names sn ON sv.spec_name_id = sn.id
WHERE sv.id IN (${placeholders})
ORDER BY sn.sort_order, sv.sort_order`,
specValueIds
);
combination.spec_details = specDetails;
} else {
combination.spec_details = [];
}
// 计算实际价格
combination.actual_price = combination.base_price + (combination.price_adjustment || 0);
combination.actual_points_price = combination.base_points_price + (combination.points_adjustment || 0);
combination.actual_rongdou_price = combination.base_rongdou_price + (combination.rongdou_adjustment || 0);
combination.is_available = combination.stock > 0;
res.json({
success: true,
data: combination
});
2025-09-24 10:02:03 +08:00
} catch (error) {
console.error('获取规格组合详情失败:', error);
res.status(500).json({success: false, message: '获取规格组合详情失败'});
2025-09-24 10:02:03 +08:00
}
});
2025-09-24 10:02:03 +08:00
router.delete('/combinations/:id', auth, adminAuth, async (req, res) => {
try {
const {id} = req.params;
// 检查规格组合是否存在
const [existingCombination] = await getDB().execute(
'SELECT id FROM product_spec_combinations WHERE id = ?',
[id]
);
if (existingCombination.length === 0) {
return res.status(404).json({success: false, message: '规格组合不存在'});
}
// 删除规格组合
await getDB().execute(
'DELETE FROM product_spec_combinations WHERE id = ?',
[id]
);
res.json({
success: true,
message: '规格组合删除成功'
});
} catch (error) {
console.error('删除规格组合失败:', error);
res.status(500).json({success: false, message: '删除规格组合失败'});
2025-09-24 10:02:03 +08:00
}
});
2025-09-24 10:02:03 +08:00
router.put('/combinations/:id', auth, adminAuth, async (req, res) => {
try {
const {id} = req.params;
const {
price_adjustment,
points_adjustment,
rongdou_adjustment,
stock,
sku_code,
barcode,
weight,
volume,
status
} = req.body;
// 检查规格组合是否存在
const [existing] = await getDB().execute(
'SELECT id FROM product_spec_combinations WHERE id = ?',
[id]
);
if (existing.length === 0) {
return res.status(404).json({success: false, message: '规格组合不存在'});
}
// 构建更新字段
const updateFields = [];
const updateValues = [];
if (price_adjustment !== undefined) {
updateFields.push('price_adjustment = ?');
updateValues.push(price_adjustment);
}
if (points_adjustment !== undefined) {
updateFields.push('points_adjustment = ?');
updateValues.push(points_adjustment);
}
if (rongdou_adjustment !== undefined) {
updateFields.push('rongdou_adjustment = ?');
updateValues.push(rongdou_adjustment);
}
if (stock !== undefined) {
updateFields.push('stock = ?');
updateValues.push(stock);
}
if (sku_code !== undefined) {
updateFields.push('sku_code = ?');
updateValues.push(sku_code);
}
if (barcode !== undefined) {
updateFields.push('barcode = ?');
updateValues.push(barcode);
}
if (weight !== undefined) {
updateFields.push('weight = ?');
updateValues.push(weight);
}
if (volume !== undefined) {
updateFields.push('volume = ?');
updateValues.push(volume);
}
if (status !== undefined) {
updateFields.push('status = ?');
updateValues.push(status);
}
if (updateFields.length === 0) {
return res.status(400).json({success: false, message: '没有提供要更新的字段'});
}
updateFields.push('updated_at = NOW()');
updateValues.push(id);
const updateQuery = `UPDATE product_spec_combinations
SET ${updateFields.join(', ')}
WHERE id = ?`;
await getDB().execute(updateQuery, updateValues);
res.json({
success: true,
message: '规格组合更新成功'
});
} catch (error) {
console.error('更新规格组合失败:', error);
res.status(500).json({success: false, message: '更新规格组合失败'});
2025-09-24 10:02:03 +08:00
}
});
2025-09-24 10:02:03 +08:00
router.post('/combinations', auth, adminAuth, async (req, res) => {
try {
const {
product_id,
spec_values,
price_adjustment = 0,
points_adjustment = 0,
rongdou_adjustment = 0,
stock = 0,
sku_code,
barcode,
weight,
volume
} = req.body;
if (!product_id || !spec_values || !Array.isArray(spec_values) || spec_values.length === 0) {
return res.status(400).json({success: false, message: '商品ID和规格值数组不能为空'});
}
// 生成组合键
const sortedSpecValues = [...spec_values].sort((a, b) => a - b);
const combinationKey = sortedSpecValues.join('-');
const [result] = await getDB().execute(
`INSERT INTO product_spec_combinations
(product_id, combination_key, spec_values, price_adjustment, points_adjustment,
rongdou_adjustment, stock, sku_code, barcode, weight, volume)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
product_id,
combinationKey,
JSON.stringify(sortedSpecValues),
price_adjustment,
points_adjustment,
rongdou_adjustment,
stock,
sku_code,
barcode,
weight,
volume
]
);
res.status(201).json({
success: true,
message: '规格组合创建成功',
data: {id: result.insertId, combination_key: combinationKey}
});
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
return res.status(400).json({success: false, message: '该规格组合已存在'});
}
console.error('创建规格组合失败:', error);
res.status(500).json({success: false, message: '创建规格组合失败'});
2025-09-24 10:02:03 +08:00
}
});
2025-09-24 10:02:03 +08:00
router.post('/generate-combinations', auth, adminAuth, async (req, res) => {
try {
const {product_id, spec_name_ids, default_stock = 0} = req.body;
if (!product_id || !spec_name_ids || !Array.isArray(spec_name_ids) || spec_name_ids.length === 0) {
return res.status(400).json({success: false, message: '商品ID和规格名称ID数组不能为空'});
}
// 获取每个规格名称下的所有活跃规格值
const specValueGroups = [];
for (const specNameId of spec_name_ids) {
const [specValues] = await getDB().execute(
'SELECT id FROM spec_values WHERE spec_name_id = ? AND status = "active" ORDER BY sort_order, id',
[specNameId]
);
if (specValues.length === 0) {
return res.status(400).json({
success: false,
message: `规格名称ID ${specNameId} 下没有活跃的规格值`
});
}
specValueGroups.push(specValues.map(sv => sv.id));
}
// 生成笛卡尔积
function cartesianProduct(arrays) {
return arrays.reduce((acc, curr) => {
const result = [];
acc.forEach(a => {
curr.forEach(c => {
result.push([...a, c]);
});
});
return result;
}, [[]]);
}
const combinations = cartesianProduct(specValueGroups);
// 生成所有组合键
const combinationData = combinations.map(combination => {
const sortedCombination = [...combination].sort((a, b) => a - b);
const combinationKey = sortedCombination.join('-');
return {
combination: sortedCombination,
key: combinationKey
};
2025-09-24 10:02:03 +08:00
});
// 批量检查已存在的组合
const existingKeys = new Set();
if (combinationData.length > 0) {
const keys = combinationData.map(item => item.key);
const placeholders = keys.map(() => '?').join(',');
const [existingCombinations] = await getDB().execute(
`SELECT combination_key
FROM product_spec_combinations
WHERE product_id = ?
AND combination_key IN (${placeholders})`,
[product_id, ...keys]
);
existingCombinations.forEach(row => {
existingKeys.add(row.combination_key);
});
}
// 过滤出需要插入的新组合
const newCombinations = combinationData.filter(item => !existingKeys.has(item.key));
// 批量插入新的规格组合
let createdCount = 0;
const skippedCount = combinationData.length - newCombinations.length;
if (newCombinations.length > 0) {
// 使用批量插入提高性能
const values = [];
const placeholders = [];
newCombinations.forEach(item => {
values.push(
product_id,
item.key,
JSON.stringify(item.combination),
default_stock
);
placeholders.push('(?, ?, ?, ?)');
});
const sql = `INSERT INTO product_spec_combinations
(product_id, combination_key, spec_values, stock)
VALUES ${placeholders.join(', ')}`;
const [result] = await getDB().execute(sql, values);
createdCount = result.affectedRows;
}
res.status(201).json({
success: true,
message: '规格组合生成完成',
data: {
total_combinations: combinations.length,
created: createdCount,
skipped: skippedCount
}
2025-09-24 10:02:03 +08:00
});
} catch (error) {
console.error('生成规格组合失败:', error);
res.status(500).json({success: false, message: '生成规格组合失败'});
2025-09-24 10:02:03 +08:00
}
});
module.exports = router;