You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

780 lines
27 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

const cloud = require("wx-server-sdk");
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV,
});
const db = cloud.database();
// 获取openid
const getOpenId = async () => {
// 获取基础信息
const wxContext = cloud.getWXContext();
return {
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
};
};
// 获取小程序二维码
const getMiniProgramCode = async () => {
// 获取小程序二维码的buffer
const resp = await cloud.openapi.wxacode.get({
path: "pages/index/index",
});
const { buffer } = resp;
// 将图片上传云存储空间
const upload = await cloud.uploadFile({
cloudPath: "code.png",
fileContent: buffer,
});
return upload.fileID;
};
// 创建集合
const createCollection = async () => {
try {
// 创建集合
await db.createCollection("sales");
await db.collection("sales").add({
// data 字段表示需新增的 JSON 数据
data: {
region: "华东",
city: "上海",
sales: 11,
},
});
await db.collection("sales").add({
// data 字段表示需新增的 JSON 数据
data: {
region: "华东",
city: "南京",
sales: 11,
},
});
await db.collection("sales").add({
// data 字段表示需新增的 JSON 数据
data: {
region: "华南",
city: "广州",
sales: 22,
},
});
await db.collection("sales").add({
// data 字段表示需新增的 JSON 数据
data: {
region: "华南",
city: "深圳",
sales: 22,
},
});
return {
success: true,
};
} catch (e) {
// 这里catch到的是该collection已经存在从业务逻辑上来说是运行成功的所以catch返回success给前端避免工具在前端抛出异常
return {
success: true,
data: "create collection success",
};
}
};
// 查询数据
const selectRecord = async () => {
// 返回数据库查询结果
return await db.collection("sales").get();
};
// 更新数据
const updateRecord = async (event) => {
try {
// 遍历修改数据库信息
for (let i = 0; i < event.data.length; i++) {
await db
.collection("sales")
.where({
_id: event.data[i]._id,
})
.update({
data: {
sales: event.data[i].sales,
},
});
}
return {
success: true,
data: event.data,
};
} catch (e) {
return {
success: false,
errMsg: e,
};
}
};
// 新增数据
const insertRecord = async (event) => {
try {
const insertRecord = event.data;
// 插入数据
await db.collection("sales").add({
data: {
region: insertRecord.region,
city: insertRecord.city,
sales: Number(insertRecord.sales),
},
});
return {
success: true,
data: event.data,
};
} catch (e) {
return {
success: false,
errMsg: e,
};
}
};
// 删除数据
const deleteRecord = async (event) => {
try {
await db
.collection("sales")
.where({
_id: event.data._id,
})
.remove();
return {
success: true,
};
} catch (e) {
return {
success: false,
errMsg: e,
};
}
};
// const getOpenId = require('./getOpenId/index');
// const getMiniProgramCode = require('./getMiniProgramCode/index');
// const createCollection = require('./createCollection/index');
// const selectRecord = require('./selectRecord/index');
// const updateRecord = require('./updateRecord/index');
// const sumRecord = require('./sumRecord/index');
// const fetchGoodsList = require('./fetchGoodsList/index');
// const genMpQrcode = require('./genMpQrcode/index');
// 查询T_user表数据
const queryTUser = async () => {
try {
const result = await db.collection("T_user").get();
return {
success: true,
data: result.data,
count: result.data.length
};
} catch (e) {
return {
success: false,
error: e.message
};
}
};
// 根据openid查询用户信息用于表关联
const getUserByOpenId = async (event) => {
try {
const wxContext = cloud.getWXContext();
const openid = event.openid || wxContext.OPENID;
console.log('查询用户信息openid:', openid);
if (!openid) {
return {
success: false,
error: 'openid不能为空'
};
}
// 先尝试通过_openid查询微信云开发自动添加的字段
let userResult = await db.collection("T_user").where({
_openid: openid
}).get();
// 如果没找到尝试通过其他方式查询如果T_user表中有openid字段
if (userResult.data.length === 0) {
// 这里可以根据实际需求添加其他查询方式
// 例如如果有存储openid字段可以查询
// userResult = await db.collection("T_user").where({
// openid: openid
// }).get();
}
if (userResult.data.length > 0) {
const userData = userResult.data[0];
return {
success: true,
data: {
userId: userData._id,
sno: userData.sno || '',
sname: userData.sname || '',
phone: userData.phone || '',
major: userData.major || '',
sushe: userData.sushe || '',
grade: userData.年级 || '',
avatar: userData.avatar || '',
openid: openid
}
};
} else {
return {
success: false,
error: '未找到用户信息',
openid: openid
};
}
} catch (e) {
console.error('查询用户信息失败:', e);
return {
success: false,
error: e.message
};
}
};
// 添加测试用户到T_user表
const addTestUser = async (event) => {
try {
const userData = event.userData || {
sno: "230340151",
sname: "测试用户",
phone: "13800138000",
password: "100997@mkg",
major: "计算机科学",
sushe: "1号楼 101",
年级: "大三",
avatar: "https://via.placeholder.com/100x100/4285F4/ffffff?text=T"
};
const result = await db.collection("T_user").add({
data: userData
});
return {
success: true,
data: result,
message: "测试用户添加成功"
};
} catch (e) {
return {
success: false,
error: e.message
};
}
};
// AI定价功能 - 调用Coze工作流分析商品图片
const analyzeProductPrice = async (event) => {
const axios = require('axios');
const FormData = require('form-data');
const COZE_API_TOKEN = 'pat_EB0pYMqiuAjIEnGK3i1e12RWwBjF5iZzrzMgdLQE8jNgZWVykJo6ZPGZ2YESYamq';
const COZE_UPLOAD_URL = 'https://api.coze.cn/v1/files/upload';
const COZE_WORKFLOW_URL = 'https://api.coze.cn/v1/workflow/run';
const WORKFLOW_ID = '7567021771821105167';
try {
console.log('========== AI定价功能开始 ==========');
console.log('接收到的参数:', JSON.stringify(event, null, 2));
// 获取参数
const { fileID, originalPrice, imageUrl } = event;
// 确保originalPrice是数字类型
const priceNum = originalPrice ? parseFloat(originalPrice) : null;
if (priceNum && isNaN(priceNum)) {
throw new Error('原价必须是有效的数字');
}
console.log('接收到的参数详情:');
console.log(' fileID:', fileID);
console.log(' originalPrice:', originalPrice, '类型:', typeof originalPrice);
console.log(' imageUrl:', imageUrl);
// 第一步:获取图片文件内容
let imageBuffer = null;
let fileName = 'image.jpg';
if (fileID) {
// 从云存储下载文件
console.log('步骤1: 从云存储下载文件, fileID:', fileID);
try {
if (!fileID || typeof fileID !== 'string') {
throw new Error('fileID参数无效');
}
const result = await cloud.downloadFile({
fileID: fileID
});
if (!result || !result.fileContent) {
throw new Error('下载文件结果为空');
}
imageBuffer = result.fileContent;
fileName = fileID.split('/').pop() || 'image.jpg';
// 确保imageBuffer是Buffer类型
if (!Buffer.isBuffer(imageBuffer)) {
imageBuffer = Buffer.from(imageBuffer);
}
console.log('✅ 文件下载成功,文件名:', fileName, '文件大小:', imageBuffer.length, 'bytes');
} catch (downloadError) {
console.error('❌ 下载云存储文件失败:', downloadError);
console.error('下载错误详情:', JSON.stringify(downloadError, Object.getOwnPropertyNames(downloadError)));
throw new Error('下载图片文件失败: ' + (downloadError.message || downloadError));
}
} else if (imageUrl) {
// 从URL下载图片
console.log('步骤1: 从URL下载图片, imageUrl:', imageUrl);
try {
if (!imageUrl || typeof imageUrl !== 'string') {
throw new Error('imageUrl参数无效');
}
const response = await axios.get(imageUrl, {
responseType: 'arraybuffer',
timeout: 30000 // 30秒超时
});
imageBuffer = Buffer.from(response.data);
fileName = imageUrl.split('/').pop().split('?')[0] || 'image.jpg';
console.log('✅ 图片下载成功,文件名:', fileName, '文件大小:', imageBuffer.length, 'bytes');
} catch (downloadError) {
console.error('❌ 下载图片失败:', downloadError);
console.error('下载错误详情:', downloadError.response?.data || downloadError.message);
throw new Error('下载图片失败: ' + (downloadError.message || downloadError));
}
} else {
throw new Error('缺少必要参数需要提供fileID或imageUrl');
}
// 验证图片缓冲区
if (!imageBuffer || imageBuffer.length === 0) {
throw new Error('图片数据为空');
}
// 第二步上传图片到Coze
console.log('步骤2: 上传图片到Coze');
let cozeFileId = null;
let uploadResponse = null; // 在外部声明,确保在返回时能访问
try {
if (!imageBuffer || imageBuffer.length === 0) {
throw new Error('图片数据为空,无法上传');
}
const formData = new FormData();
formData.append('file', imageBuffer, {
filename: fileName,
contentType: 'image/jpeg'
});
console.log('准备上传到Coze文件大小:', imageBuffer.length, 'bytes');
uploadResponse = await axios.post(COZE_UPLOAD_URL, formData, {
headers: {
'Authorization': `Bearer ${COZE_API_TOKEN}`,
...formData.getHeaders()
},
timeout: 60000, // 60秒超时
maxContentLength: Infinity,
maxBodyLength: Infinity
});
console.log('✅ 图片上传到Coze成功');
console.log('上传响应数据:', JSON.stringify(uploadResponse.data, null, 2));
if (uploadResponse.data.code === 0 && uploadResponse.data.data && uploadResponse.data.data.id) {
cozeFileId = uploadResponse.data.data.id;
console.log('✅ 获取到Coze file_id:', cozeFileId);
} else {
throw new Error('上传响应格式错误: ' + JSON.stringify(uploadResponse.data));
}
} catch (uploadError) {
console.error('❌ 上传图片到Coze失败:', uploadError.response?.data || uploadError.message);
throw new Error('上传图片到Coze失败: ' + (uploadError.response?.data?.msg || uploadError.message));
}
// 第三步调用Coze工作流
console.log('步骤3: 调用Coze工作流');
console.log('workflow_id:', WORKFLOW_ID);
console.log('file_id:', cozeFileId);
try {
// 根据curl示例input参数应该是数字类型原价
// curl示例: "input": 30
// 不再需要inputText直接使用priceNum
// 验证file_id
if (!cozeFileId || typeof cozeFileId !== 'string') {
throw new Error('Coze file_id无效: ' + cozeFileId);
}
// 根据API文档image参数应该是字符串内容是JSON格式的字符串
// 格式: "{\"file_id\":\"...\"}"
// 注意需要手动构建JSON字符串确保格式正确
const imageParam = JSON.stringify({ file_id: String(cozeFileId) });
// 验证image参数格式
try {
const imageObj = JSON.parse(imageParam);
if (!imageObj.file_id || typeof imageObj.file_id !== 'string') {
throw new Error('image参数格式错误: file_id无效');
}
console.log('✅ image参数格式验证通过:', imageObj);
} catch (parseError) {
console.error('❌ image参数格式验证失败:', parseError);
throw new Error('image参数格式错误: ' + parseError.message);
}
// 构建请求对象
const workflowRequest = {
workflow_id: String(WORKFLOW_ID),
parameters: {
image: imageParam, // JSON字符串: "{\"file_id\":\"7567222758887850038\"}"
input: Number(priceNum || 0) // 数字类型原价根据curl示例
}
};
// 验证最终请求格式
const testSerialize = JSON.stringify(workflowRequest);
console.log('测试序列化结果:', testSerialize);
const testParse = JSON.parse(testSerialize);
console.log('测试解析结果:', JSON.stringify(testParse, null, 2));
// 验证解析后的image参数格式
const parsedImage = JSON.parse(testParse.parameters.image);
console.log('解析后的image对象:', parsedImage);
if (!parsedImage.file_id) {
throw new Error('序列化后image参数格式错误');
}
// 打印最终发送的请求(序列化后的格式)
const requestBody = JSON.stringify(workflowRequest);
console.log('========== 请求参数详情 ==========');
console.log('工作流请求数据(序列化前):', JSON.stringify(workflowRequest, null, 2));
console.log('工作流请求数据序列化后将发送给API:', requestBody);
console.log('参数类型检查:');
console.log(' workflow_id类型:', typeof workflowRequest.workflow_id);
console.log(' workflow_id值:', workflowRequest.workflow_id);
console.log(' image类型:', typeof workflowRequest.parameters.image);
console.log(' image值:', workflowRequest.parameters.image);
console.log(' image值长度:', workflowRequest.parameters.image.length);
console.log(' image值解析:', JSON.parse(workflowRequest.parameters.image));
console.log(' input类型:', typeof workflowRequest.parameters.input);
console.log(' input值:', workflowRequest.parameters.input);
console.log('===================================');
// 验证input是数字类型
if (typeof workflowRequest.parameters.input !== 'number') {
throw new Error('input参数必须是数字类型当前类型: ' + typeof workflowRequest.parameters.input);
}
// 模拟curl命令格式用于对比
const curlFormat = JSON.stringify({
workflow_id: workflowRequest.workflow_id,
parameters: {
image: workflowRequest.parameters.image,
input: workflowRequest.parameters.input
}
}, null, 2);
console.log('预期格式类似curl:', curlFormat);
console.log('✅ 参数格式验证通过符合curl示例格式');
const workflowResponse = await axios.post(COZE_WORKFLOW_URL, workflowRequest, {
headers: {
'Authorization': `Bearer ${COZE_API_TOKEN}`,
'Content-Type': 'application/json'
},
timeout: 120000 // 120秒超时因为AI分析可能需要较长时间
});
console.log('✅ Coze工作流HTTP请求成功');
console.log('HTTP状态码:', workflowResponse.status);
console.log('工作流响应数据:', JSON.stringify(workflowResponse.data, null, 2));
// 检查响应状态
if (workflowResponse.data.code !== 0) {
const errorData = workflowResponse.data;
console.error('❌ Coze API返回错误');
console.error('错误代码:', errorData.code);
console.error('错误消息:', errorData.msg);
console.error('错误详情:', errorData.detail);
console.error('调试URL:', errorData.debug_url);
// 如果是参数错误,提供更详细的诊断信息
if (errorData.code === 4000) {
console.error('参数错误诊断:');
console.error(' 发送的请求数据:', JSON.stringify(workflowRequest, null, 2));
console.error(' image参数原始值:', imageParam);
console.error(' input参数原始值:', inputText);
}
throw new Error(`Coze API错误 (${errorData.code}): ${errorData.msg || '未知错误'}`);
}
// 解析返回结果
let parsedResult = {};
if (workflowResponse.data.data) {
try {
// data字段是字符串需要解析
const dataStr = typeof workflowResponse.data.data === 'string'
? workflowResponse.data.data
: JSON.stringify(workflowResponse.data.data);
const parsedData = JSON.parse(dataStr);
// 解析output字段
if (parsedData.output) {
let outputStr = typeof parsedData.output === 'string'
? parsedData.output
: JSON.stringify(parsedData.output);
console.log('步骤3.1: 原始output字符串:', outputStr);
// 尝试解析output中的JSON字符串
try {
// 处理多重转义的情况
// 如果output是被双重转义的JSON字符串需要先解析一次
if (outputStr.startsWith('"') && outputStr.endsWith('"')) {
try {
outputStr = JSON.parse(outputStr);
console.log('步骤3.2: 解析外层引号后的output:', outputStr);
} catch (e) {
// 如果解析失败,继续使用原始字符串
console.warn('步骤3.2: 解析外层引号失败,继续使用原始字符串');
}
}
// 处理转义字符
let cleanedOutput = outputStr;
// 如果仍然是字符串,尝试多次解析转义
if (typeof cleanedOutput === 'string') {
// 尝试解析转义的JSON字符串
try {
// 如果看起来像转义的JSON尝试解析
if (cleanedOutput.includes('\\"')) {
cleanedOutput = cleanedOutput.replace(/\\"/g, '"').replace(/\\n/g, '\n');
}
} catch (e) {
// 忽略解析错误
}
}
console.log('步骤3.3: 清理后的output:', cleanedOutput);
console.log('步骤3.3: output类型:', typeof cleanedOutput);
// 使用正则表达式提取字段值
// 匹配格式: "name":"值" 或 name:"值" 或 name=值
const outputContent = typeof cleanedOutput === 'string' ? cleanedOutput : JSON.stringify(cleanedOutput);
const nameMatch = outputContent.match(/["']?name["']?\s*[:=]\s*["']([^"']+)["']/);
const priceMatch = outputContent.match(/["']?price["']?\s*[:=]\s*["']([^"']+)["']/);
const chengseMatch = outputContent.match(/["']?chengse["']?\s*[:=]\s*["']([^"']+)["']/);
const gradeMatch = outputContent.match(/["']?grade["']?\s*[:=]\s*["']([^"']+)["']/);
const suggMatch = outputContent.match(/["']?sugg["']?\s*[:=]\s*["']([^"']+)["']/);
console.log('步骤3.4: 正则匹配结果:');
console.log(' name:', nameMatch ? nameMatch[1] : '未匹配');
console.log(' price:', priceMatch ? priceMatch[1] : '未匹配');
console.log(' chengse:', chengseMatch ? chengseMatch[1] : '未匹配');
console.log(' grade:', gradeMatch ? gradeMatch[1] : '未匹配');
console.log(' sugg:', suggMatch ? (suggMatch[1].substring(0, 50) + '...') : '未匹配');
// 如果正则匹配失败尝试直接解析为JSON对象
let parsedOutput = null;
try {
// 尝试构建一个完整的JSON对象
if (outputContent.includes('name') && outputContent.includes('price')) {
// 尝试提取JSON部分
const jsonMatch = outputContent.match(/\{[\s\S]*\}/);
if (jsonMatch) {
parsedOutput = JSON.parse(jsonMatch[0]);
console.log('步骤3.5: 成功解析为JSON对象:', parsedOutput);
} else {
// 尝试手动构建JSON字符串
const jsonStr = '{' + outputContent.replace(/\n/g, ',') + '}';
try {
parsedOutput = JSON.parse(jsonStr);
console.log('步骤3.5: 成功构建并解析JSON对象:', parsedOutput);
} catch (e) {
// 忽略错误
}
}
}
} catch (e) {
console.warn('步骤3.5: 解析JSON对象失败:', e.message);
}
// 优先使用解析后的对象,否则使用正则匹配
parsedResult = {
productName: parsedOutput?.name || nameMatch?.[1] || '--',
suggestedPrice: parsedOutput?.price || priceMatch?.[1] || '0.00',
conditionLevel: parsedOutput?.chengse || chengseMatch?.[1] || '--',
aiScore: parsedOutput?.grade || gradeMatch?.[1] || '--',
analysisReport: parsedOutput?.sugg || suggMatch?.[1] || 'AI分析完成',
rawOutput: outputContent
};
console.log('✅ 步骤3.6: 最终解析结果:', JSON.stringify(parsedResult, null, 2));
} catch (parseError) {
console.warn('⚠️ 解析output失败使用原始数据:', parseError.message);
console.warn('解析错误堆栈:', parseError.stack);
parsedResult = {
productName: '--',
suggestedPrice: originalPrice ? (originalPrice * 0.8).toFixed(2) : '0.00',
conditionLevel: '--',
aiScore: '--',
analysisReport: outputStr.substring(0, 200),
rawOutput: outputStr
};
}
} else {
parsedResult = {
productName: '--',
suggestedPrice: originalPrice ? (originalPrice * 0.8).toFixed(2) : '0.00',
conditionLevel: '--',
aiScore: '--',
analysisReport: 'AI分析完成但未获取到详细信息',
rawOutput: dataStr
};
}
} catch (parseError) {
console.error('❌ 解析响应数据失败:', parseError);
parsedResult = {
productName: '--',
suggestedPrice: originalPrice ? (originalPrice * 0.8).toFixed(2) : '0.00',
conditionLevel: '--',
aiScore: '--',
analysisReport: '解析响应数据失败: ' + parseError.message,
rawResponse: workflowResponse.data
};
}
} else {
console.warn('⚠️ 工作流响应中没有data字段');
console.warn('响应内容:', JSON.stringify(workflowResponse.data, null, 2));
// 如果响应中没有data返回默认结果
parsedResult = {
productName: '--',
suggestedPrice: originalPrice ? (originalPrice * 0.8).toFixed(2) : '0.00',
conditionLevel: '--',
aiScore: '--',
analysisReport: 'AI分析完成但未获取到详细信息',
rawResponse: workflowResponse.data
};
}
console.log('========== AI定价功能完成 ==========');
return {
success: true,
data: parsedResult,
debug: {
uploadResponse: uploadResponse ? uploadResponse.data : null,
workflowResponse: workflowResponse.data
}
};
} catch (workflowError) {
console.error('❌ 调用Coze工作流失败');
console.error('错误对象:', workflowError);
console.error('错误响应:', workflowError.response?.data);
console.error('错误状态码:', workflowError.response?.status);
console.error('错误消息:', workflowError.message);
// 提取详细的错误信息
let errorMsg = '调用Coze工作流失败';
if (workflowError.response?.data) {
const errorData = workflowError.response.data;
errorMsg = errorData.msg || errorData.message || errorMsg;
if (errorData.detail) {
errorMsg += '\n详情: ' + JSON.stringify(errorData.detail);
}
if (errorData.code) {
errorMsg += '\n错误代码: ' + errorData.code;
}
} else {
errorMsg += ': ' + workflowError.message;
}
throw new Error(errorMsg);
}
} catch (error) {
console.error('========== AI定价功能出错 ==========');
console.error('错误信息:', error.message);
console.error('错误堆栈:', error.stack);
return {
success: false,
error: error.message,
details: error.response?.data || error.stack
};
}
};
// 云函数入口函数
exports.main = async (event, context) => {
try {
console.log('云函数被调用type:', event.type);
console.log('event参数:', JSON.stringify(event, null, 2));
switch (event.type) {
case "getOpenId":
return await getOpenId();
case "getMiniProgramCode":
return await getMiniProgramCode();
case "createCollection":
return await createCollection();
case "selectRecord":
return await selectRecord();
case "updateRecord":
return await updateRecord(event);
case "insertRecord":
return await insertRecord(event);
case "deleteRecord":
return await deleteRecord(event);
case "queryTUser":
return await queryTUser();
case "addTestUser":
return await addTestUser(event);
case "analyzeProductPrice":
return await analyzeProductPrice(event);
case "getUserByOpenId":
return await getUserByOpenId(event);
default:
return {
success: false,
error: '未知的操作类型: ' + event.type
};
}
} catch (error) {
console.error('========== 云函数入口函数错误 ==========');
console.error('错误信息:', error.message);
console.error('错误堆栈:', error.stack);
console.error('event:', JSON.stringify(event, null, 2));
return {
success: false,
error: error.message || '云函数执行失败',
details: error.stack,
type: event.type
};
}
};