From 631e3caa4c83366b9e01b96cf5e1471b33391471 Mon Sep 17 00:00:00 2001 From: ingkele <598792849@qq.com> Date: Wed, 14 Oct 2026 21:55:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=90=88=E5=90=8C?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=E4=B8=8E?= =?UTF-8?q?=E7=A7=AF=E5=88=86=E4=BD=93=E7=B3=BB=EF=BC=8C=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E6=A8=A1=E5=9D=97=E4=B8=8E=E4=BD=99=E9=A2=9D?= =?UTF-8?q?=E6=B5=81=E6=B0=B4=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jjxReviewStream/config.json | 12 + .../cloudfunctions/jjxReviewStream/index.js | 106 ++ .../jjxReviewStream/package.json | 6 + .../cloudfunctions/jjxReviewTimer/index.js | 239 +++ .../jjxReviewTimer/jjxReviewTimer.param.json | 4 + .../jjxReviewTimer/package.json | 19 + .../cloudfunctions/router/package-lock.json | 45 +- .../cloudfunctions/router/package.json | 2 +- .../admin/finance/balance/sys/getActionLog.js | 98 +- .../admin/finance/balance/sys/getSummary.js | 22 +- .../service/admin/finance/balance/sys/util.js | 104 -- .../admin/finance/balance/sys/withdraw.js | 70 +- .../router/service/client/ai/pub/chat.js | 5 +- .../service/client/ai/pub/doReviewTimer.js | 53 + .../service/client/ai/pub/getMyReports.js | 30 + .../service/client/ai/pub/getReviewResult.js | 27 + .../service/client/ai/pub/jjxDoReview.js | 217 +++ .../router/service/client/ai/pub/review.js | 252 ++++ .../router/service/client/payment.js | 1318 +++++++++-------- .../service/client/points/kh/createOrder.js | 39 + .../router/service/client/points/kh/deduct.js | 47 + .../service/client/points/kh/getBalance.js | 37 + .../router/service/client/points/kh/getLog.js | 40 + .../service/client/points/kh/recharge.js | 55 + .../service/common/公共逻辑(公共接口).md | 0 .../router/service/template/test/sys/test.js | 20 + .../database/jjx-points-log.schema.json | 1 + .../database/jjx-review-records.schema.json | 1 + .../database/jjx-user-points.schema.json | 1 + 29 files changed, 2008 insertions(+), 862 deletions(-) create mode 100644 uniCloud-tcb/cloudfunctions/jjxReviewStream/config.json create mode 100644 uniCloud-tcb/cloudfunctions/jjxReviewStream/index.js create mode 100644 uniCloud-tcb/cloudfunctions/jjxReviewStream/package.json create mode 100644 uniCloud-tcb/cloudfunctions/jjxReviewTimer/index.js create mode 100644 uniCloud-tcb/cloudfunctions/jjxReviewTimer/jjxReviewTimer.param.json create mode 100644 uniCloud-tcb/cloudfunctions/jjxReviewTimer/package.json create mode 100644 uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/doReviewTimer.js create mode 100644 uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/getMyReports.js create mode 100644 uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/getReviewResult.js create mode 100644 uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js create mode 100644 uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/review.js create mode 100644 uniCloud-tcb/cloudfunctions/router/service/client/points/kh/createOrder.js create mode 100644 uniCloud-tcb/cloudfunctions/router/service/client/points/kh/deduct.js create mode 100644 uniCloud-tcb/cloudfunctions/router/service/client/points/kh/getBalance.js create mode 100644 uniCloud-tcb/cloudfunctions/router/service/client/points/kh/getLog.js create mode 100644 uniCloud-tcb/cloudfunctions/router/service/client/points/kh/recharge.js create mode 100644 uniCloud-tcb/cloudfunctions/router/service/common/公共逻辑(公共接口).md create mode 100644 uniCloud-tcb/cloudfunctions/router/service/template/test/sys/test.js create mode 100644 uniCloud-tcb/database/jjx-points-log.schema.json create mode 100644 uniCloud-tcb/database/jjx-review-records.schema.json create mode 100644 uniCloud-tcb/database/jjx-user-points.schema.json diff --git a/uniCloud-tcb/cloudfunctions/jjxReviewStream/config.json b/uniCloud-tcb/cloudfunctions/jjxReviewStream/config.json new file mode 100644 index 0000000..26bcc55 --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/jjxReviewStream/config.json @@ -0,0 +1,12 @@ +{ + "triggers": [ + { + "name": "http", + "type": "http", + "config": { + "path": "/review-stream", + "method": ["POST"] + } + } + ] +} \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/jjxReviewStream/index.js b/uniCloud-tcb/cloudfunctions/jjxReviewStream/index.js new file mode 100644 index 0000000..6abcd12 --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/jjxReviewStream/index.js @@ -0,0 +1,106 @@ +'use strict'; + +exports.main = async (event, context) => { + // uniCloud HTTP 触发器:event 本身就是请求信息 + // event.httpMethod = 'GET'/'POST'/'OPTIONS' + // event.body = 请求体 + // event.headers = 请求头 + // context.callbackWaitsForEmptyEventLoop = true 保持连接 + + const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization', + 'Access-Control-Max-Age': '86400' + }; + + // 处理 CORS 预检 + if (event.httpMethod === 'OPTIONS') { + return { + statusCode: 204, + headers: corsHeaders, + body: '' + }; + } + + // 设置等待空事件循环(保持 SSE 连接) + context.callbackWaitsForEmptyEventLoop = false; + + // 解析请求体 + let body = {}; + try { + const raw = event.body || ''; + if (event.isBase64Encoded) { + body = JSON.parse(Buffer.from(raw, 'base64').toString()); + } else { + try { body = JSON.parse(raw); } catch (e) { + const params = new URLSearchParams(raw); + for (const [k, v] of params) body[k] = v; + } + } + } catch (e) {} + + const { file_id, user_id, file_name, file_size, record_id } = body; + + if (!file_id) { + return { + statusCode: 400, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + body: JSON.stringify({ error: '缺少 file_id' }) + }; + } + + const COZE_TOKEN = 'sat_OiXlI4Da5Q99fxmul6pNfExmA6hR898UbxBDwrvjZ638APxRSlFnXbz6zyFedKNG'; + const BOT_ID = '7689821072762011698'; + + const objectString = JSON.stringify([ + { type: 'file', file_id: file_id }, + { type: 'text', text: '请审查这份合同' } + ]); + + // 调用扣子(流式) + try { + const chatRes = await uniCloud.httpclient.request('https://api.coze.cn/v3/chat', { + method: 'POST', + dataType: 'text', + contentType: 'json', + timeout: 300000, + headers: { 'Authorization': 'Bearer ' + COZE_TOKEN }, + data: { + bot_id: BOT_ID, + user_id: user_id || 'anonymous', + stream: true, + auto_save_history: true, + additional_messages: [{ + role: 'user', + content: objectString, + content_type: 'object_string' + }] + } + }); + + // 返回 SSE 格式 + const text = chatRes.data || ''; + const sseBody = text.split('\n').map(line => { + if (line.startsWith('data:')) return line + '\n\n'; + return ''; + }).join('') + 'data: [DONE]\n\n'; + + return { + statusCode: 200, + headers: { + ...corsHeaders, + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive' + }, + body: sseBody + }; + } catch (e) { + return { + statusCode: 500, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + body: JSON.stringify({ error: e.message }) + }; + } +}; \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/jjxReviewStream/package.json b/uniCloud-tcb/cloudfunctions/jjxReviewStream/package.json new file mode 100644 index 0000000..85ab71b --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/jjxReviewStream/package.json @@ -0,0 +1,6 @@ +{ + "name": "jjxReviewStream", + "version": "1.0.0", + "description": "合同审查流式输出云函数", + "main": "index.js" +} \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/jjxReviewTimer/index.js b/uniCloud-tcb/cloudfunctions/jjxReviewTimer/index.js new file mode 100644 index 0000000..1034e80 --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/jjxReviewTimer/index.js @@ -0,0 +1,239 @@ +'use strict'; + +// 定时任务:每分钟检查 reviewing 状态的记录,触发审查 +const db = uniCloud.database(); +const _ = db.command; + +// 导入审查执行函数(从 router 的 service 目录) +// 注意:定时任务是独立云函数,不能直接 require router 的 service +// 所以把审查逻辑直接写在这里 + +const COZE_TOKEN = 'sat_OiXlI4Da5Q99fxmul6pNfExmA6hR898UbxBDwrvjZ638APxRSlFnXbz6zyFedKNG'; +const BOT_ID = '7689821072762011698'; + +async function doReview(record) { + const { _id: record_id, file_id, user_id, file_name, file_size } = record; + + const objectString = JSON.stringify([ + { type: 'file', file_id: file_id }, + { type: 'text', text: '请审查这份合同' } + ]); + + try { + // 1. 非流式发起对话(立即返回 chat_id) + const chatRes = await uniCloud.httpclient.request('https://api.coze.cn/v3/chat', { + method: 'POST', + dataType: 'json', + contentType: 'json', + timeout: 30000, + headers: { 'Authorization': 'Bearer ' + COZE_TOKEN }, + data: { + bot_id: BOT_ID, + user_id: user_id || 'anonymous', + stream: false, + auto_save_history: true, + additional_messages: [{ + role: 'user', + content: objectString, + content_type: 'object_string' + }] + } + }); + + const chatData = chatRes.data && chatRes.data.data; + const chatId = chatData && chatData.id; + const conversationId = chatData && chatData.conversation_id; + console.log('[审查] chat_id:', chatId, 'conversation_id:', conversationId); + + if (!chatId) { + throw new Error('未获取到 chat_id: ' + JSON.stringify(chatRes.data)); + } + + // 保存 chat_id 和 conversation_id 到数据库 + await db.collection('jjx-review-records').doc(record_id).update({ + chat_id: chatId, + conversation_id: conversationId || '', + update_time: new Date() + }); + console.log('[审查] 已保存 chat_id:', chatId); + + // 2. 轮询对话状态(每5秒查一次,最多5分钟) + let answer = ''; + let tokenCount = 0; + for (let i = 0; i < 60; i++) { + await new Promise(r => setTimeout(r, 5000)); + + const statusRes = await uniCloud.httpclient.request( + 'https://api.coze.cn/v3/chat/retrieve?chat_id=' + chatId + '&conversation_id=' + conversationId, + { method: 'GET', dataType: 'json', headers: { 'Authorization': 'Bearer ' + COZE_TOKEN }, timeout: 15000 } + ); + + const status = statusRes.data && statusRes.data.data && statusRes.data.data.status; + console.log('[审查] 第', i + 1, '次轮询, 状态:', status); + + if (status === 'completed') { + // 保存 token 消耗信息 + const usage = statusRes.data && statusRes.data.data && statusRes.data.data.usage; + tokenCount = (usage && usage.token_count) || 0; + console.log('[审查] Token 消耗:', tokenCount); + + // 3. 获取消息列表 + const msgRes = await uniCloud.httpclient.request( + 'https://api.coze.cn/v3/chat/message/list?chat_id=' + chatId + '&conversation_id=' + conversationId, + { method: 'GET', dataType: 'json', headers: { 'Authorization': 'Bearer ' + COZE_TOKEN }, timeout: 15000 } + ); + + const messages = msgRes.data && msgRes.data.data; + if (Array.isArray(messages)) { + for (const msg of messages) { + if (msg.type === 'answer' && msg.role === 'assistant' && msg.content) { + answer = msg.content; // answer 类型的完整内容 + } + if (msg.type === 'tool_response' && msg.content) { + try { + const toolData = JSON.parse(msg.content); + if (toolData.url) answer += '\n\n📎 报告下载:' + toolData.url; + } catch (e) {} + } + } + } + break; + } + + if (status === 'failed') { + throw new Error('对话失败'); + } + } + + if (!answer) answer = '审查未能获取结果,请稍后重试'; + console.log('[审查] 结果长度:', answer.length); + + // 计算消耗积分 + // 积分 = token消耗 / 100(最低1积分) + const pointsUsed = Math.max(1, Math.round(tokenCount / 100)); + console.log('[积分] token_count:', tokenCount, '→ 扣除积分:', pointsUsed); + + // 扣积分 + const userPoints = await db.collection('jjx-user-points').doc(user_id).get(); + if (userPoints.data.length && userPoints.data[0].balance >= pointsUsed) { + await db.collection('jjx-user-points').doc(user_id).update({ + balance: _.inc(-pointsUsed), + total_spent: _.inc(pointsUsed), + update_time: new Date() + }); + const updatedPoints = await db.collection('jjx-user-points').doc(user_id).get(); + await db.collection('jjx-points-log').add({ + user_id: user_id, + type: 'spend', + amount: -pointsUsed, + balance_after: updatedPoints.data[0] ? updatedPoints.data[0].balance : 0, + reason: '合同审查 — ' + (file_name || '') + '(' + tokenCount + ' token)', + biz_type: 'contract_review', + biz_id: record_id, + _add_time: new Date() + }); + } + + // 保存 HTML 报告到云存储 + let htmlUrl = ''; + try { + let htmlContent = answer; + const htmlMatch = answer.match(/```html\s*([\s\S]*?)```/); + if (htmlMatch && htmlMatch[1]) htmlContent = htmlMatch[1].trim(); + + if (htmlContent.includes(' { + // 先把卡在 processing 超过10分钟的记录重置为 reviewing(超时保护) + const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000); + const stuckRecords = await db.collection('jjx-review-records') + .where({ status: 'processing', update_time: _.lt(tenMinutesAgo) }) + .get(); + for (const stuck of stuckRecords.data) { + await db.collection('jjx-review-records').doc(stuck._id).update({ + status: 'reviewing', + update_time: new Date() + }); + } + + // 查询所有 reviewing 状态的记录(超过1分钟的,避免重复触发) + const oneMinuteAgo = new Date(Date.now() - 60 * 1000); + + const pendingRecords = await db.collection('jjx-review-records') + .where({ + status: 'reviewing', + review_started_at: _.lt(oneMinuteAgo) + }) + .limit(5) // 每次最多处理5条 + .get(); + + if (!pendingRecords.data.length) { + return { code: 0, msg: '无待处理记录' }; + } + + console.log('发现待处理审查记录:', pendingRecords.data.length, '条'); + + // 逐条处理(串行,避免并发问题) + const results = []; + for (const record of pendingRecords.data) { + // 先标记为 processing,避免重复触发 + await db.collection('jjx-review-records').doc(record._id).update({ + status: 'processing', + update_time: new Date() + }); + + try { + const result = await doReview(record); + results.push({ record_id: record._id, ...result }); + } catch (e) { + // 审查失败,重置为 reviewing 以便下次重试 + await db.collection('jjx-review-records').doc(record._id).update({ + status: 'reviewing', + update_time: new Date() + }); + console.error('审查异常,已重置状态:', record._id, e.message); + results.push({ record_id: record._id, code: -1, msg: e.message }); + } + } + + return { code: 0, msg: '处理完成', data: results }; +}; \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/jjxReviewTimer/jjxReviewTimer.param.json b/uniCloud-tcb/cloudfunctions/jjxReviewTimer/jjxReviewTimer.param.json new file mode 100644 index 0000000..0f56b5c --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/jjxReviewTimer/jjxReviewTimer.param.json @@ -0,0 +1,4 @@ +// 本文件中的json内容将在云函数【运行】时作为参数传给云函数。 +// 配置教程参考:https://uniapp.dcloud.net.cn/uniCloud/rundebug.html#runparam +{ +} \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/jjxReviewTimer/package.json b/uniCloud-tcb/cloudfunctions/jjxReviewTimer/package.json new file mode 100644 index 0000000..3ba759e --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/jjxReviewTimer/package.json @@ -0,0 +1,19 @@ +{ + "name": "jjxReviewTimer", + "version": "1.0.0", + "description": "合同审查定时任务", + "main": "index.js", + "cloudfunction-config": { + "memorySize": 256, + "timeout": 600, + "triggers": [ + { + "name": "myTrigger", + "type": "timer", + "config": "0 */1 * * * * *" + } + ], + "path": "", + "runtime": "Nodejs16" + } +} \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/router/package-lock.json b/uniCloud-tcb/cloudfunctions/router/package-lock.json index 9f76028..3efc7fd 100644 --- a/uniCloud-tcb/cloudfunctions/router/package-lock.json +++ b/uniCloud-tcb/cloudfunctions/router/package-lock.json @@ -35,63 +35,64 @@ }, "node_modules/charenc": { "version": "0.0.2", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", "engines": { "node": "*" } }, "node_modules/crypt": { "version": "0.0.2", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", "engines": { "node": "*" } }, "node_modules/crypto-js": { "version": "4.2.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "deprecated": "Active development of CryptoJS has been discontinued. This library is no longer maintained." }, "node_modules/dayjs": { - "version": "1.11.10", - "license": "MIT" - }, - "node_modules/debug": { - "version": "3.2.7", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } + "version": "1.11.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", + "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==" }, "node_modules/is-buffer": { "version": "1.1.6", - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "node_modules/md5": { "version": "2.3.0", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", "dependencies": { "charenc": "0.0.2", "crypt": "0.0.2", "is-buffer": "~1.1.6" } }, - "node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, "node_modules/node-ensure": { "version": "0.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", + "integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw==" }, "node_modules/pdf-parse": { - "version": "1.1.1", - "license": "MIT", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-1.1.4.tgz", + "integrity": "sha512-XRIRcLgk6ZnUbsHsYXExMw+krrPE81hJ6FQPLdBNhhBefqIQKXu/WeTgNBGSwPrfU0v+UCEwn7AoAUOsVKHFvQ==", "dependencies": { - "debug": "^3.1.0", "node-ensure": "^0.0.0" }, "engines": { "node": ">=6.8.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/mehmet-kozan" } }, "node_modules/uni-config-center": { diff --git a/uniCloud-tcb/cloudfunctions/router/package.json b/uniCloud-tcb/cloudfunctions/router/package.json index 55135b8..5f2cfc7 100644 --- a/uniCloud-tcb/cloudfunctions/router/package.json +++ b/uniCloud-tcb/cloudfunctions/router/package.json @@ -23,7 +23,7 @@ "concurrency": 1, "memorySize": 512, "path": "/http/router", - "timeout": 60, + "timeout": 600, "triggers": [], "keepRunningAfterReturn": false }, diff --git a/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/getActionLog.js b/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/getActionLog.js index f40266a..4e9c3e9 100644 --- a/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/getActionLog.js +++ b/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/getActionLog.js @@ -8,17 +8,15 @@ module.exports = { * @param {String} accNo 账号 * @param {String} reqDate 请求日期,格式:yyyyMMdd * @param {String} reqJournalNo 请求流水号 - * @param {String} logId 可选,流水ID,传入后会回写状态 */ main: async (event) => { let { data = {}, util } = event; let { vk } = util; - let { accNo, reqDate, reqJournalNo, logId } = data; + let { accNo, reqDate, reqJournalNo } = data; accNo = String(accNo || '').trim(); reqDate = String(reqDate || '').trim(); reqJournalNo = String(reqJournalNo || '').trim(); - logId = String(logId || '').trim(); if (!accNo) { return { @@ -48,83 +46,43 @@ module.exports = { }; } - let res = await balanceUtil.requestActionLog({ - vk, - accNo, - reqDate, - reqJournalNo + let res = await vk.request({ + url: 'http://jjx-union-pay.gxwebsoft.com/api/union-pay-jjx/getActionLog', + method: 'POST', + dataType: 'json', + header: { + 'content-type': 'application/json; charset=utf-8' + }, + data: { + accNo, + reqDate, + reqJournalNo + }, + timeout: 60000 }); if (res && typeof res === 'object' && typeof res.code === 'undefined') { res.code = 0; } - const status = balanceUtil.getActionStatus(res); - const statusText = balanceUtil.getActionStatusTextByStatus(status); - - if (status !== null) { - let logInfo = null; - if (logId) { - logInfo = await vk.baseDao.findByWhereJson({ - dbName: 'jjx-balance-log', - whereJson: { - _id: logId - } - }); - } - if (!logInfo) { - logInfo = await vk.baseDao.findByWhereJson({ - dbName: 'jjx-balance-log', - whereJson: { - srcReqId: reqJournalNo, - merchantId: accNo - } - }); - } - - if (logInfo && logInfo._id) { - logId = logInfo._id; - const prevStatus = Number(logInfo.status); - const nextStatus = Number(status); - const updateData = { - status: nextStatus - }; - - // 新提现单(balanceDeducted === false):成功时再扣减余额缓存 - // 旧提现单无该字段:创建时已扣减,这里只更新状态 - const needDeductOnSuccess = logInfo.balanceDeducted === false - && prevStatus === 1 - && nextStatus === 2; - - if (needDeductOnSuccess) { - const amount = balanceUtil.normalizeAmount(logInfo.amount); - await balanceUtil.applyMerchantBalanceDelta({ - vk, - merchantId: accNo, - delta: -amount, - lastLogId: logId - }); - updateData.balanceDeducted = true; - } - - await vk.baseDao.updateById({ - dbName: 'jjx-balance-log', - id: logId, - dataJson: updateData - }); - } - } - const responseObjects = balanceUtil.getRemoteResponseObjects(res); + const list = balanceUtil.pickFirstDefined([ + Array.isArray(res.list) ? res.list : undefined, + Array.isArray(res.rows) ? res.rows : undefined, + Array.isArray(res.data) ? res.data : undefined, + Array.isArray(res.data && res.data.list) ? res.data.list : undefined, + Array.isArray(res.data && res.data.rows) ? res.data.rows : undefined, + Array.isArray(res.info && res.info.list) ? res.info.list : undefined, + Array.isArray(res.info && res.info.rows) ? res.info.rows : undefined, + Array.isArray(res.content && res.content.list) ? res.content.list : undefined, + Array.isArray(res.content && res.content.rows) ? res.content.rows : undefined + ]) || []; + if (responseObjects.data && typeof responseObjects.data === 'object' && !Array.isArray(responseObjects.data)) { - responseObjects.data.status = status; - responseObjects.data.statusText = statusText; - responseObjects.data.logId = logId; + responseObjects.data.list = list; } else if (res && typeof res === 'object') { res.data = { - status, - statusText, - logId + list }; } diff --git a/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/getSummary.js b/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/getSummary.js index e5ec531..3bc896e 100644 --- a/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/getSummary.js +++ b/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/getSummary.js @@ -1,5 +1,5 @@ 'use strict'; -const { BALANCE_DOC_ID, getBalanceInfoList, getAvailableWithdrawBalance, normalizeAmount } = require('./util.js'); +const { BALANCE_DOC_ID, getBalanceInfoList } = require('./util.js'); module.exports = { /** @@ -25,24 +25,6 @@ module.exports = { } const balanceInfoList = getBalanceInfoList(info.balanceInfo || {}); - const enrichedList = []; - for (let i = 0; i < balanceInfoList.length; i++) { - const item = balanceInfoList[i]; - const { - pendingAmount, - availableBalance - } = await getAvailableWithdrawBalance({ - vk, - merchantId: item.accNo, - merchantBalance: item.balance - }); - enrichedList.push({ - ...item, - balance: normalizeAmount(item.balance), - pendingAmount, - availableBalance - }); - } return { code: 0, @@ -50,7 +32,7 @@ module.exports = { data: { balance: Number(Number(info.balance || 0).toFixed(2)), updateTime: info.updateTime || info._update_time_str || info._add_time_str || '', - balanceInfoList: enrichedList + balanceInfoList } }; } diff --git a/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/util.js b/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/util.js index c69236d..5fef182 100644 --- a/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/util.js +++ b/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/util.js @@ -213,64 +213,6 @@ function getActionStatusTextByStatus(status) { return statusMap[Number(status)] || ''; } -async function applyMerchantBalanceDelta({ - vk, - transaction, - merchantId, - delta, - lastLogId = '', - updateTime = dayjs().format('YYYY-MM-DD HH:mm:ss') -}) { - const accNo = String(merchantId || '').trim(); - if (!accNo) { - throw new Error('提现账号不能为空'); - } - const changeAmount = normalizeAmount(delta); - const balanceRecord = await ensureBalanceRecord({ transaction, vk }); - const balanceInfoMap = buildBalanceInfoMap(balanceRecord.balanceInfo || {}); - const currentMerchantInfo = balanceInfoMap[accNo] || { - accNo, - merchantName: getMerchantLabel(accNo), - balance: 0, - updateTime: '' - }; - const currentMerchantBalance = normalizeAmount(currentMerchantInfo.balance || 0); - const nextMerchantBalance = normalizeAmount(currentMerchantBalance + changeAmount); - if (nextMerchantBalance < 0) { - throw new Error('余额不足'); - } - const nextBalanceInfoMap = { - ...balanceInfoMap, - [accNo]: { - accNo, - merchantName: currentMerchantInfo.merchantName || getMerchantLabel(accNo), - balance: nextMerchantBalance, - updateTime - } - }; - const nextTotalBalance = normalizeAmount(Number(balanceRecord.balance || 0) + changeAmount); - const dataJson = { - balance: nextTotalBalance, - balanceInfo: nextBalanceInfoMap, - updateTime - }; - if (lastLogId) { - dataJson.lastLogId = lastLogId; - } - await vk.baseDao.updateById({ - db: transaction, - dbName: 'jjx-balance', - id: balanceRecord._id, - dataJson - }); - return { - balance: nextTotalBalance, - merchantBalance: nextMerchantBalance, - updateTime, - balanceInfoMap: nextBalanceInfoMap - }; -} - async function syncMerchantBalance({ vk, accNo, @@ -381,49 +323,6 @@ async function checkBalanceAvailable({ transaction, vk, changeAmount, excludeLog }; } -/** - * 统计指定商户处理中的提现金额(status=1) - */ -async function getPendingWithdrawAmount({ transaction, vk, merchantId }) { - const accNo = String(merchantId || '').trim(); - if (!accNo) { - return 0; - } - const res = await vk.baseDao.select({ - db: transaction, - dbName: 'jjx-balance-log', - whereJson: { - merchantId: accNo, - type: 'expense', - source: 'withdraw', - status: 1 - }, - pageIndex: 1, - pageSize: 1000, - getCount: false - }); - const rows = (res && res.rows) || []; - return normalizeAmount(rows.reduce((sum, item) => sum + Number(item.amount || 0), 0)); -} - -/** - * 可提现余额 = 当前余额 - 处理中提现金额 - */ -async function getAvailableWithdrawBalance({ transaction, vk, merchantId, merchantBalance }) { - const pendingAmount = await getPendingWithdrawAmount({ - transaction, - vk, - merchantId - }); - const balance = normalizeAmount(merchantBalance); - const availableBalance = normalizeAmount(Math.max(0, balance - pendingAmount)); - return { - balance, - pendingAmount, - availableBalance - }; -} - async function updateBalanceWithDelta({ transaction, vk, delta, lastLogId = '' }) { const balanceInfo = await ensureBalanceRecord({ transaction, vk }); const nextBalance = Number((Number(balanceInfo.balance || 0) + Number(delta || 0)).toFixed(2)); @@ -473,13 +372,10 @@ module.exports = { parseOrderSet, getActionStatus, getActionStatusTextByStatus, - applyMerchantBalanceDelta, syncMerchantBalance, ensureBalanceRecord, recalculateBalance, checkBalanceAvailable, - getPendingWithdrawAmount, - getAvailableWithdrawBalance, updateBalanceWithDelta, rollbackTransaction }; diff --git a/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/withdraw.js b/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/withdraw.js index 3cadddc..8163780 100644 --- a/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/withdraw.js +++ b/uniCloud-tcb/cloudfunctions/router/service/admin/finance/balance/sys/withdraw.js @@ -1,4 +1,5 @@ 'use strict'; +const dayjs = require('dayjs'); const balanceUtil = require('./util.js'); const merchantMap = balanceUtil.MERCHANT_MAP; @@ -33,28 +34,11 @@ module.exports = { updateTime: '' }; const merchantBalance = balanceUtil.normalizeAmount(merchantBalanceInfo.balance || 0); - const { - pendingAmount, - availableBalance - } = await balanceUtil.getAvailableWithdrawBalance({ - transaction, - vk, - merchantId, - merchantBalance - }); - if (availableBalance <= 0) { - throw new Error( - pendingAmount > 0 - ? `可提现余额不足(处理中提现¥${pendingAmount.toFixed(2)}已占用)` - : '当前企业余额小于等于0,无法提现' - ); + if (merchantBalance <= 0) { + throw new Error('当前企业余额小于等于0,无法提现'); } - if (amount > availableBalance) { - throw new Error( - pendingAmount > 0 - ? `提现金额不能大于可提现余额¥${availableBalance.toFixed(2)}(已排除处理中提现¥${pendingAmount.toFixed(2)})` - : '提现金额不能大于当前企业余额' - ); + if (amount > merchantBalance) { + throw new Error('提现金额不能大于当前企业余额'); } const description = String(data.description || '').trim() || `余额提现-${merchantMap[merchantId]}`; @@ -96,28 +80,52 @@ module.exports = { merchantId, merchantName: merchantMap[merchantId], srcReqId: requestRes.srcReqId, - status: 1, - // 新逻辑:处理中不扣余额,成功后再扣;用于兼容旧数据(旧单默认已扣) - balanceDeducted: false + status: 1 }; + const changeAmount = balanceUtil.buildBalanceChange(logData); - await vk.baseDao.add({ + await balanceUtil.checkBalanceAvailable({ + transaction, + vk, + changeAmount + }); + + const addRes = await vk.baseDao.add({ db: transaction, dbName: 'jjx-balance-log', dataJson: logData }); - // 处理中提现不立刻扣减余额缓存,避免与远端余额同步互相覆盖; - // 可提现额度通过「余额 - 处理中提现」计算,防止重复提现超总额。 + const updateTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); + const nextMerchantBalance = Number((merchantBalance + changeAmount).toFixed(2)); + const nextBalance = Number((Number(balanceRecord.balance || 0) + changeAmount).toFixed(2)); + await vk.baseDao.updateById({ + db: transaction, + dbName: 'jjx-balance', + id: balanceRecord._id, + dataJson: { + balance: nextBalance, + lastLogId: addRes.id, + updateTime, + balanceInfo: { + ...balanceInfoMap, + [merchantId]: { + accNo: merchantId, + merchantName: merchantMap[merchantId], + balance: nextMerchantBalance, + updateTime + } + } + } + }); + await transaction.commit(); return { code: 0, msg: '', data: { - balance: merchantBalance, - pendingAmount: balanceUtil.normalizeAmount(pendingAmount + amount), - availableBalance: balanceUtil.normalizeAmount(Math.max(0, availableBalance - amount)), - merchantBalance + balance: nextBalance, + merchantBalance: nextMerchantBalance } }; } catch (err) { diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js index 6ee3733..c43baba 100644 --- a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js +++ b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js @@ -25,7 +25,7 @@ module.exports = { } }); - let answer = '暂时无法回复,请拨打 400-686-5559 咨询'; + let answer = ''; let conversationId = ''; const text = res.data || ''; const lines = text.split('\n'); @@ -37,10 +37,11 @@ module.exports = { const p = JSON.parse(d); if (p.conversation_id) conversationId = p.conversation_id; if (p.type === 'answer' && p.content) { - answer = p.content; + answer += p.content; // 拼接,不是覆盖 } } catch (e) { /* 跳过 */ } } + if (!answer) answer = '暂时无法回复,请拨打 400-686-5559 咨询'; return { code: 0, diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/doReviewTimer.js b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/doReviewTimer.js new file mode 100644 index 0000000..397658b --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/doReviewTimer.js @@ -0,0 +1,53 @@ +// client/ai/pub/doReviewTimer.js +module.exports = { + main: async (event) => { + let { data = {} } = event; + const { record_id, file_id, user_id, file_name, file_size } = data; + + if (!record_id || !file_id) return { code: -1, msg: '参数错误' }; + + const db = uniCloud.database(); + + // 检查是否已经在审查中(防止重复触发) + const record = await db.collection('jjx-review-records').doc(record_id).get(); + if (record.data.length && record.data[0].status === 'reviewing') { + // 检查是否刚触发(5分钟内不重复触发) + const lastUpdate = record.data[0].update_time; + if (lastUpdate && (Date.now() - new Date(lastUpdate).getTime() < 5 * 60 * 1000)) { + return { code: 0, msg: '已在审查中,跳过' }; + } + } + + // 更新状态为审查中 + await db.collection('jjx-review-records').doc(record_id).update({ + status: 'reviewing', + review_started_at: new Date(), + update_time: new Date() + }); + + // 调用实际的审查逻辑 + try { + const doReview = require('./jjxDoReview.js'); + await doReview.main({ + data: { + record_id, + file_id, + user_id: user_id || 'anonymous', + file_name: file_name || '', + file_size: file_size || 0 + } + }); + return { code: 0, msg: '审查完成' }; + } catch (e) { + console.error('审查失败:', e); + // 更新状态为失败 + await db.collection('jjx-review-records').doc(record_id).update({ + status: 'failed', + result: '审查失败: ' + e.message, + error: e.message, + update_time: new Date() + }); + return { code: -1, msg: '审查失败: ' + e.message }; + } + } +}; \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/getMyReports.js b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/getMyReports.js new file mode 100644 index 0000000..8b8cdf8 --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/getMyReports.js @@ -0,0 +1,30 @@ +module.exports = { + main: async (event) => { + let { data = {}, userInfo, util } = event; + let { db } = util; + const { pageIndex = 1, pageSize = 50 } = data; + + const uid = (data && data.uid) || ''; + if (!uid) return { code: -1, msg: '未登录' }; + + const where = { user_id: uid }; + + const countRes = await db.collection('jjx-review-records').where(where).count(); + + const listRes = await db.collection('jjx-review-records') + .where(where) + .orderBy('_add_time', 'desc') + .skip((pageIndex - 1) * pageSize) + .limit(pageSize) + .get(); + + return { + code: 0, + msg: 'ok', + data: { + total: countRes.total, + rows: listRes.data + } + }; + } +}; \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/getReviewResult.js b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/getReviewResult.js new file mode 100644 index 0000000..266c9bf --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/getReviewResult.js @@ -0,0 +1,27 @@ +// 云函数:client/ai/pub/getReviewResult +module.exports = { + main: async (event) => { + let { data = {}, userInfo, util } = event; + let { db } = util; + const { record_id } = data; + + if (!record_id) return { code: -1, msg: '缺少 record_id' }; + + const record = await db.collection('jjx-review-records').doc(record_id).get(); + if (!record.data.length) return { code: -1, msg: '记录不存在' }; + + const rec = record.data[0]; + + return { + code: 0, + data: { + status: rec.status, + result: rec.result || '', + points_used: rec.points_used || 0, + html_url: rec.html_url || '', + contract_info: rec.contract_info || {}, + error: rec.error || '' + } + }; + } +}; \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js new file mode 100644 index 0000000..86b3b98 --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js @@ -0,0 +1,217 @@ +// client/ai/pub/jjxDoReview.js +module.exports = { + main: async (event) => { + let { data = {}, userInfo } = event; + const { record_id, file_id, user_id, file_name, file_size } = data; + + if (!record_id || !file_id) return { code: -1, msg: '参数错误' }; + + const COZE_TOKEN = 'sat_OiXlI4Da5Q99fxmul6pNfExmA6hR898UbxBDwrvjZ638APxRSlFnXbz6zyFedKNG'; + const BOT_ID = '7689821072762011698'; + const db = uniCloud.database(); + const _ = db.command; + + try { + // 构造请求 + const objectString = JSON.stringify([ + { type: 'file', file_id: file_id }, + { type: 'text', text: '请审查这份合同' } + ]); + + // 调用 Coze 接口(流式模式) + const chatRes = await uniCloud.httpclient.request('https://api.coze.cn/v3/chat', { + method: 'POST', + dataType: 'stream', // 关键:改成 stream + contentType: 'json', + timeout: 300000, // 5分钟超时 + headers: { + 'Authorization': 'Bearer ' + COZE_TOKEN, + 'Accept': 'text/event-stream' + }, + data: { + bot_id: BOT_ID, + user_id: user_id || 'anonymous', + stream: true, + auto_save_history: true, + additional_messages: [{ + role: 'user', + content: objectString, + content_type: 'object_string' + }] + } + }); + + // 从流中逐步解析 + const stream = chatRes.data || chatRes.res; + let buffer = ''; + let answer = ''; + let conversationId = ''; + let lastUpdateLen = 0; + + // 返回 Promise,等待流结束 + return await new Promise((resolve, reject) => { + + stream.on('data', (chunk) => { + buffer += chunk.toString(); + const blocks = buffer.split('\n\n'); + buffer = blocks.pop(); + + for (const block of blocks) { + const lines = block.split('\n'); + let eventName = ''; + let dataStr = ''; + + for (const line of lines) { + if (line.startsWith('event:')) eventName = line.slice(6).trim(); + if (line.startsWith('data:')) dataStr = line.slice(5).trim(); + } + + if (!dataStr || dataStr === '[DONE]' || dataStr === '"[DONE]"') continue; + + try { + const p = JSON.parse(dataStr); + if (p.conversation_id) conversationId = p.conversation_id; + + // 处理回答内容 + if (p.type === 'answer' && p.content) { + answer += p.content; + + // 每积累 200 字更新一次数据库(前端轮询能看到进度) + if (answer.length - lastUpdateLen >= 200) { + lastUpdateLen = answer.length; + db.collection('jjx-review-records').doc(record_id).update({ + result: answer, + status: 'reviewing', + update_time: new Date() + }).catch(() => {}); + } + } + + // 处理工具响应(报告下载链接) + if (p.type === 'tool_response' && p.content) { + try { + const toolData = JSON.parse(p.content); + if (toolData.url) answer += '\n\n📎 报告下载:' + toolData.url; + } catch (e) { + const urlMatch = p.content.match(/https?:\/\/[^\s\)\]\"\<]+\.pdf[^\s\)\]\"\<]*/i); + if (urlMatch) answer += '\n\n📎 报告下载:' + urlMatch[0]; + } + } + } catch (e) {} + } + }); + + stream.on('end', async () => { + if (!answer) { + answer = '审查未能获取结果,请稍后重试'; + } + + try { + // 计算消耗积分 + const pointsUsed = Math.max(150, Math.ceil((file_size || 0) / (100 * 1024)) * 150); + + // 扣积分 + const userPoints = await db.collection('jjx-user-points').doc(user_id).get(); + if (userPoints.data.length && userPoints.data[0].balance >= pointsUsed) { + await db.collection('jjx-user-points').doc(user_id).update({ + balance: _.inc(-pointsUsed), + total_spent: _.inc(pointsUsed), + update_time: new Date() + }); + + const updatedPoints = await db.collection('jjx-user-points').doc(user_id).get(); + await db.collection('jjx-points-log').add({ + user_id: user_id, + type: 'spend', + amount: -pointsUsed, + balance_after: updatedPoints.data[0] ? updatedPoints.data[0].balance : 0, + reason: '合同审查 — ' + (file_name || ''), + biz_type: 'contract_review', + biz_id: record_id, + _add_time: new Date() + }); + } + + // 保存 HTML 报告到云存储 + let htmlUrl = ''; + try { + // 如果回复本身就是 HTML(包含 或 ) + let htmlContent = answer; + const htmlMatch = answer.match(/```html\s*([\s\S]*?)```/); + if (htmlMatch && htmlMatch[1]) { + htmlContent = htmlMatch[1].trim(); + } + + if (htmlContent.includes(' { + await db.collection('jjx-review-records').doc(record_id).update({ + status: 'failed', + result: '审查失败: ' + err.message, + error: err.message, + update_time: new Date() + }); + reject(err); + }); + + }); + + } catch (e) { + // 审查失败,更新记录 + await db.collection('jjx-review-records').doc(record_id).update({ + status: 'failed', + result: '审查失败: ' + e.message, + error: e.message, + update_time: new Date() + }); + return { code: -1, msg: '审查失败: ' + e.message }; + } + } +}; \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/review.js b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/review.js new file mode 100644 index 0000000..ced0ff8 --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/review.js @@ -0,0 +1,252 @@ +// client/ai/pub/review.js +// Markdown 转 HTML +function mdToHtml(md) { + if (!md) return ''; + let html = md; + // 标题 + html = html.replace(/^####\s+(.+)$/gm, '
$1
'); + html = html.replace(/^###\s+(.+)$/gm, '

$1

'); + html = html.replace(/^##\s+(.+)$/gm, '

$1

'); + html = html.replace(/^#\s+(.+)$/gm, '

$1

'); + // 加粗 + html = html.replace(/\*\*(.*?)\*\*/g, '$1'); + // 链接 + html = html.replace(/(https?:\/\/[^\s\)\]\"\<]+)/g, '$1'); + // 代码块 + html = html.replace(/```[\w]*\n([\s\S]*?)```/g, '
$1
'); + // 行内代码 + html = html.replace(/`([^`]+)`/g, '$1'); + // 列表 + html = html.replace(/^[\-\•]\s+(.+)$/gm, '
  • $1
  • '); + html = html.replace(/^(\d+)\.\s+(.+)$/gm, '
  • $1. $2
  • '); + // 换行 + html = html.replace(/\n\n/g, '

    '); + html = html.replace(/\n/g, '
    '); + html = '

    ' + html + '

    '; + // 清理空段落 + html = html.replace(/

    \s*<\/p>/g, ''); + return html; +} + +// 生成精美 HTML 报告 +function generateHtmlReport(markdown, fileName, reportDate, info) { + const riskLevel = info.risk_level || '待评估'; + const riskSummary = info.risk_summary || ''; + const riskCount = info.risk_count || { high: 0, medium: 0, low: 0 }; + const contractName = info.contract_name || fileName || '合同审查报告'; + const contractType = info.contract_type || ''; + + // 风险等级颜色 + let riskColor = '#67c23a'; + if (riskLevel.includes('高')) riskColor = '#f56c6c'; + else if (riskLevel.includes('中高')) riskColor = '#e6a23c'; + else if (riskLevel.includes('中')) riskColor = '#e6a23c'; + else if (riskLevel.includes('中低')) riskColor = '#409eff'; + + return ` + + + + +${contractName} - 审查报告 + + + +

    +
    +

    📋 合同审查报告

    +
    ${contractName} | ${reportDate}
    +
    +
    +
    +
    综合风险等级
    +
    ${riskLevel}
    +
    +
    +
    风险统计
    +
    ${riskCount.high||0} / ${riskCount.medium||0} / ${riskCount.low||0}
    +
    +
    +
    合同类型
    +
    ${contractType || '待识别'}
    +
    +
    +
    +

    总体评估

    +

    ${riskSummary || '暂无总体评估'}

    +

    详细审查意见

    + ${mdToHtml(markdown.replace(/```json[\s\S]*?```/, ''))} +
    + ⚠️ 免责声明:本报告由 AI 系统自动生成,仅供参考,不构成正式法律意见。涉及重大金额或复杂交易的合同,建议咨询执业律师。 +
    +
    + +
    + +`; +} + +module.exports = { + main: async (event) => { + let { data = {}, userInfo } = event; + const COZE_TOKEN = 'sat_OiXlI4Da5Q99fxmul6pNfExmA6hR898UbxBDwrvjZ638APxRSlFnXbz6zyFedKNG'; + const BOT_ID = '7689821072762011698'; + const db = uniCloud.database(); + const _ = db.command; + + let fileId = data.file_id; + + // ===== 模式1:上传文件 + 创建待审查记录 ===== + if (data.upload_only && data.file_base64) { + const fileBuffer = Buffer.from(data.file_base64, 'base64'); + const boundary = '----FormBoundary' + Date.now(); + const header = Buffer.from( + '--' + boundary + '\r\n' + + 'Content-Disposition: form-data; name="file"; filename="' + (data.file_name || 'file') + '"\r\n' + + 'Content-Type: application/octet-stream\r\n\r\n' + ); + const footer = Buffer.from('\r\n--' + boundary + '--\r\n'); + const uploadBody = Buffer.concat([header, fileBuffer, footer]); + + try { + const uploadRes = await uniCloud.httpclient.request('https://api.coze.cn/v1/files/upload', { + method: 'POST', + dataType: 'json', + headers: { + 'Authorization': 'Bearer ' + COZE_TOKEN, + 'Content-Type': 'multipart/form-data; boundary=' + boundary, + 'Content-Length': uploadBody.length.toString() + }, + content: uploadBody, + timeout: 600000 + }); + if (uploadRes.data.code !== 0) { + return { code: -1, msg: '文件上传失败: ' + uploadRes.data.msg }; + } + fileId = uploadRes.data.data.id; + + // 同时上传到 uniCloud 云存储(用于查看/下载) + let fileUrl = ''; + try { + const cloudPath = 'review-files/' + Date.now() + '_' + (data.file_name || 'file'); + const uploadFileRes = await uniCloud.uploadFile({ + cloudPath: cloudPath, + fileContent: fileBuffer + }); + if (uploadFileRes.fileID) { + const urlRes = await uniCloud.getTempFileURL({ + fileList: [uploadFileRes.fileID] + }); + if (urlRes.fileList && urlRes.fileList[0]) { + fileUrl = urlRes.fileList[0].tempFileURL; + } + } + } catch (e) { + console.log('云存储上传失败(不影响审查):', e.message); + } + + // 创建待审查记录 + const recordRes = await db.collection('jjx-review-records').add({ + user_id: (data && data.uid) || 'anonymous', + file_name: data.file_name || '', + file_size: data.file_size || 0, + file_id: fileId, + file_url: fileUrl, + chat_id: '', + conversation_id: '', + status: 'uploaded', + result: '', + points_used: 0, + _add_time: new Date(), + update_time: new Date() + }); + + return { code: 0, msg: 'ok', data: { file_id: fileId, record_id: recordRes.id, file_url: fileUrl } }; + } catch (e) { + return { code: -1, msg: '上传异常: ' + e.message }; + } + } + + // ===== 模式2:发起审查(只更新状态,立即返回)===== + if (!fileId) return { code: -1, msg: '缺少 file_id' }; + + const recordId = data.record_id; + if (!recordId) return { code: -1, msg: '缺少 record_id' }; + + // 检查当前状态 + const record = await db.collection('jjx-review-records').doc(recordId).get(); + if (record.data.length) { + const rec = record.data[0]; + + // 已完成,直接返回结果 + if (rec.status === 'completed') { + return { + code: 0, msg: 'ok', + data: { + record_id: recordId, + status: 'completed', + result: rec.result, + points_used: rec.points_used, + html_url: rec.html_url || '', + contract_info: rec.contract_info || {} + } + }; + } + + // 正在审查中,直接返回 + if (rec.status === 'reviewing') { + return { code: 0, msg: 'ok', data: { record_id: recordId, status: 'reviewing' } }; + } + + // 失败,返回错误 + if (rec.status === 'failed') { + return { code: 0, msg: 'ok', data: { record_id: recordId, status: 'failed', result: rec.result || '审查失败' } }; + } + } + + // 更新状态为审查中 + await db.collection('jjx-review-records').doc(recordId).update({ + file_id: fileId, + file_name: data.file_name || '', + file_size: data.file_size || 0, + status: 'reviewing', + review_started_at: new Date(), + update_time: new Date() + }); + + // ===== 立即返回,审查由定时任务触发 ===== + return { + code: 0, msg: 'ok', + data: { record_id: recordId, status: 'reviewing' } + }; + } +}; \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/payment.js b/uniCloud-tcb/cloudfunctions/router/service/client/payment.js index 01a6c3d..0d07e87 100644 --- a/uniCloud-tcb/cloudfunctions/router/service/client/payment.js +++ b/uniCloud-tcb/cloudfunctions/router/service/client/payment.js @@ -1,28 +1,36 @@ 'use strict'; const dayjs = require("dayjs"); -const { BALANCE_DOC_ID, normalizeAmount } = require("../admin/finance/balance/sys/util.js"); +const { + BALANCE_DOC_ID, + normalizeAmount +} = require("../admin/finance/balance/sys/util.js"); const notaryPay = require("./order/kh/util/notaryPay"); var vk; // 全局vk实例 const orderTypeList = [ - '商城订单', - '金久信自营增值服务', - '保证金', - '会员升级', - '询价订单', - '加工订单', - '旧货订单', - '报价次数', - '现货广告', - '旧货加急', - '旧货次数', - '金久信学堂下载次数', - '司法调证', - '金久信签约宝次数', - '金久信自营询价', + '商城订单', + '金久信自营增值服务', + '保证金', + '会员升级', + '询价订单', + '加工订单', + '旧货订单', + '报价次数', + '现货广告', + '旧货加急', + '旧货次数', + '金久信学堂下载次数', + '司法调证', + '金久信签约宝次数', + '金久信自营询价', + '', + '', + '', + '', + 'AI积分充值' ]; // 涉及的表名 const dbName = { - //test: "vk-test", // 测试表 + //test: "vk-test", // 测试表 }; var db = uniCloud.database(); // 全局数据库引用 @@ -30,21 +38,21 @@ var _ = db.command; // 数据库操作符 var $ = _.aggregate; // 聚合查询操作符 function pickFirstDefined() { - for (let i = 0; i < arguments.length; i++) { - const value = arguments[i]; - if (value !== undefined && value !== null && value !== '') { - return value; - } - } - return undefined; + for (let i = 0; i < arguments.length; i++) { + const value = arguments[i]; + if (value !== undefined && value !== null && value !== '') { + return value; + } + } + return undefined; } function toFenAmount(value) { - const amount = Number(value); - if (!Number.isFinite(amount)) { - throw new Error(`支付金额格式不正确: ${value}`); - } - return Number((amount / 100).toFixed(2)); + const amount = Number(value); + if (!Number.isFinite(amount)) { + throw new Error(`支付金额格式不正确: ${value}`); + } + return Number((amount / 100).toFixed(2)); } /** @@ -52,612 +60,698 @@ function toFenAmount(value) { * 文档地址:https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#内置权限 */ var cloudObject = { - isCloudObject: true, // 标记为云对象模式 - /** - * 请求前处理,主要用于调用方法之前进行预处理,一般用于拦截器、统一的身份验证、参数校验、定义全局对象等。 - * 文档地址:https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#before-预处理 - */ - _before: async function () { - vk = this.vk; // 将vk定义为全局对象 - // let { customUtil, uniID, config, pubFun } = this.getUtil(); // 获取工具包 - }, - /** - * 请求后处理,主要用于处理本次调用方法的返回结果或者抛出的错误 - * 文档地址:https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#after-后处理 - */ - _after: async function (options) { - let {err, res} = options; - if (err) { - return; // 如果方法抛出错误,直接return;不处理 - } - return res; - }, - /** - * 获取列表 - * @url client/payment.getList 前端调用的url参数地址 - */ - pub_payNotify: async function (data) { - let res = {code: 0, msg: ''}; - // 业务逻辑开始----------------------------------------------------------- - console.log("pay notify data", data) - let orderNo = data.billNo - if (!orderNo) orderNo = data.merOrderId - let payTime = null, payAmount = null, payNo = null - if (data.billPayment) { - const billPayment = JSON.parse(data.billPayment) - payTime = pickFirstDefined(billPayment.payTime, data.payTime, data.createTime) - payAmount = toFenAmount(pickFirstDefined(billPayment.receiptAmount, billPayment.totalAmount, data.receiptAmount, data.totalAmount)) - payNo = pickFirstDefined(billPayment.paySeqId, billPayment.seqId, data.seqId, data.targetOrderId) - } else { - payTime = pickFirstDefined(data.payTime, data.createTime) - payAmount = toFenAmount(pickFirstDefined(data.receiptAmount, data.totalAmount)) - payNo = pickFirstDefined(data.seqId, data.targetOrderId) - } - const isPaid = data.billStatus === 'PAID' || data.status === 'TRADE_SUCCESS' + isCloudObject: true, // 标记为云对象模式 + /** + * 请求前处理,主要用于调用方法之前进行预处理,一般用于拦截器、统一的身份验证、参数校验、定义全局对象等。 + * 文档地址:https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#before-预处理 + */ + _before: async function() { + vk = this.vk; // 将vk定义为全局对象 + // let { customUtil, uniID, config, pubFun } = this.getUtil(); // 获取工具包 + }, + /** + * 请求后处理,主要用于处理本次调用方法的返回结果或者抛出的错误 + * 文档地址:https://vkdoc.fsq.pub/client/uniCloud/cloudfunctions/cloudObject.html#after-后处理 + */ + _after: async function(options) { + let { + err, + res + } = options; + if (err) { + return; // 如果方法抛出错误,直接return;不处理 + } + return res; + }, + /** + * 获取列表 + * @url client/payment.getList 前端调用的url参数地址 + */ + pub_payNotify: async function(data) { + console.log("=== 支付回调触发 ===", JSON.stringify(data)); + console.log("订单号:", data.billNo || data.merOrderId); + console.log("支付状态:", data.billStatus); + let res = { + code: 0, + msg: '' + }; + // 业务逻辑开始----------------------------------------------------------- + console.log("pay notify data", data) + let orderNo = data.billNo + if (!orderNo) orderNo = data.merOrderId + let payTime = null, + payAmount = null, + payNo = null + if (data.billPayment) { + const billPayment = JSON.parse(data.billPayment) + payTime = pickFirstDefined(billPayment.payTime, data.payTime, data.createTime) + payAmount = toFenAmount(pickFirstDefined(billPayment.receiptAmount, billPayment.totalAmount, data + .receiptAmount, data.totalAmount)) + payNo = pickFirstDefined(billPayment.paySeqId, billPayment.seqId, data.seqId, data.targetOrderId) + } else { + payTime = pickFirstDefined(data.payTime, data.createTime) + payAmount = toFenAmount(pickFirstDefined(data.receiptAmount, data.totalAmount)) + payNo = pickFirstDefined(data.seqId, data.targetOrderId) + } + const isPaid = data.billStatus === 'PAID' || data.status === 'TRADE_SUCCESS' - const payRecord = await vk.baseDao.findByWhereJson({ - dbName: "jjx-order-pay-list", - whereJson: { - orderNo - } - }); - if (payRecord && isPaid) { - const order = await vk.baseDao.findById({ - dbName: "jjx-order", - id: payRecord.orderId - }); - if (!payRecord.hasPaid) { - await vk.baseDao.updateById({ - dbName: "jjx-order-pay-list", - id: payRecord._id, - dataJson: { - hasPaid: true, - paidAt: payTime, - payAmount, - payNo, - paidVoucher: "", - buyerRemark: payRecord.isNotaryFullPay ? "公证支付" : payRecord.buyerRemark, - sellerConfirm: payRecord.isNotaryFullPay ? true : payRecord.sellerConfirm, - confirmAt: payRecord.isNotaryFullPay ? payTime : payRecord.confirmAt, - payNotifyData: data - } - }); - if (payRecord.isNotaryFullPay) { - await notaryPay.notifyPaySuccess({ - vk, - payRecord, - order - }); - } - } - if (order) { - const orderUpdate = { - orderStatus: order.orderStatus > 0 ? order.orderStatus : 1, - payTime, - payAmount, - payNo, - }; - if (payRecord.isNotaryFullPay) { - orderUpdate.notaryPayStatus = 1; - orderUpdate.notaryPaidAt = payTime; - orderUpdate.notaryPaidAmount = payAmount; - orderUpdate.notaryPayListId = payRecord._id; - orderUpdate.notaryPayOrderNo = orderNo; - orderUpdate.payStatus = 2; - } - await vk.baseDao.updateById({ - dbName: "jjx-order", - id: order._id, - dataJson: orderUpdate - }); - } - return res; - } + const payRecord = await vk.baseDao.findByWhereJson({ + dbName: "jjx-order-pay-list", + whereJson: { + orderNo + } + }); + if (payRecord && isPaid) { + const order = await vk.baseDao.findById({ + dbName: "jjx-order", + id: payRecord.orderId + }); + if (!payRecord.hasPaid) { + await vk.baseDao.updateById({ + dbName: "jjx-order-pay-list", + id: payRecord._id, + dataJson: { + hasPaid: true, + paidAt: payTime, + payAmount, + payNo, + paidVoucher: "", + buyerRemark: payRecord.isNotaryFullPay ? "公证支付" : payRecord.buyerRemark, + sellerConfirm: payRecord.isNotaryFullPay ? true : payRecord.sellerConfirm, + confirmAt: payRecord.isNotaryFullPay ? payTime : payRecord.confirmAt, + payNotifyData: data + } + }); + if (payRecord.isNotaryFullPay) { + await notaryPay.notifyPaySuccess({ + vk, + payRecord, + order + }); + } + } + if (order) { + const orderUpdate = { + orderStatus: order.orderStatus > 0 ? order.orderStatus : 1, + payTime, + payAmount, + payNo, + }; + if (payRecord.isNotaryFullPay) { + orderUpdate.notaryPayStatus = 1; + orderUpdate.notaryPaidAt = payTime; + orderUpdate.notaryPaidAmount = payAmount; + orderUpdate.notaryPayListId = payRecord._id; + orderUpdate.notaryPayOrderNo = orderNo; + orderUpdate.payStatus = 2; + } + await vk.baseDao.updateById({ + dbName: "jjx-order", + id: order._id, + dataJson: orderUpdate + }); + } + return res; + } - let order = await vk.baseDao.findByWhereJson({ - dbName: "jjx-order", - whereJson: { - orderNo - } - }); + let order = await vk.baseDao.findByWhereJson({ + dbName: "jjx-order", + whereJson: { + orderNo + } + }); - // type 0: 商品 - // type 1: 自营 - // type 2: 保证金 - // type 3: 会员升级 - // type 7: 询价次数 + // type 0: 商品 + // type 1: 自营 + // type 2: 保证金 + // type 3: 会员升级 + // type 7: 询价次数 - // orderStatus 0待支付 1已支付待发货 2已发货待收货 3已收货待完成 4已完成 5退款审核中 6退款成功 7退款失败 - if (order && order.orderStatus === 0 && isPaid) { + // orderStatus 0待支付 1已支付待发货 2已发货待收货 3已收货待完成 4已完成 5退款审核中 6退款成功 7退款失败 + if (order && order.orderStatus === 0 && isPaid) { - await vk.baseDao.updateById({ - dbName: "jjx-order", - id: order._id, - dataJson: { - orderStatus: 1, - payTime, - payAmount, - payNo, - } - }); + await vk.baseDao.updateById({ + dbName: "jjx-order", + id: order._id, + dataJson: { + orderStatus: 1, + payTime, + payAmount, + payNo, + } + }); - if (![4, 5, 6].includes(order.type)) { - const incomeAmount = normalizeAmount(payAmount); - const orderTypeName = orderTypeList[order.type] || `类型${order.type}`; - const balanceInfo = await vk.baseDao.findByWhereJson({ - dbName: "jjx-balance", - whereJson: { - _id: BALANCE_DOC_ID - } - }); - if (!balanceInfo) { - throw new Error(`jjx-balance余额记录不存在: ${BALANCE_DOC_ID}`); - } + if (![4, 5, 6].includes(order.type)) { + const incomeAmount = normalizeAmount(payAmount); + const orderTypeName = orderTypeList[order.type] || `类型${order.type}`; + const balanceInfo = await vk.baseDao.findByWhereJson({ + dbName: "jjx-balance", + whereJson: { + _id: BALANCE_DOC_ID + } + }); + if (!balanceInfo) { + throw new Error(`jjx-balance余额记录不存在: ${BALANCE_DOC_ID}`); + } - await vk.baseDao.add({ - dbName: "jjx-balance-log", - dataJson: { - description: `${orderTypeName}-订单支付入账-${order.orderNo}`, - amount: incomeAmount, - type: "income", - logTime: payTime ? new Date(payTime).getTime() : Date.now(), - operatorId: order.uid || "", - source: "order_pay", - orderId: order._id, - orderNo: order.orderNo - } - }); + await vk.baseDao.add({ + dbName: "jjx-balance-log", + dataJson: { + description: `${orderTypeName}-订单支付入账-${order.orderNo}`, + amount: incomeAmount, + type: "income", + logTime: payTime ? new Date(payTime).getTime() : Date.now(), + operatorId: order.uid || "", + source: "order_pay", + orderId: order._id, + orderNo: order.orderNo + } + }); - await vk.baseDao.updateById({ - dbName: "jjx-balance", - id: BALANCE_DOC_ID, - dataJson: { - balance: _.inc(incomeAmount), - _update_time: Date.now() - } - }); - } + await vk.baseDao.updateById({ + dbName: "jjx-balance", + id: BALANCE_DOC_ID, + dataJson: { + balance: _.inc(incomeAmount), + _update_time: Date.now() + } + }); + } - if ([2, 3, 11, 12, 13].includes(order.type)) { - await vk.baseDao.updateById({ - dbName: "jjx-order", - id: order._id, - dataJson: { - orderStatus: 4 - } - }); - } + if ([2, 3, 11, 12, 13,20].includes(order.type)) { + await vk.baseDao.updateById({ + dbName: "jjx-order", + id: order._id, + dataJson: { + orderStatus: 4 + } + }); + } - if (order.type === 0) { - const goodsList = await vk.baseDao.select({ - dbName: "jjx-order-goods", - whereJson: { - orderId: order._id, - } - }); - for (let i = 0; i < goodsList.rows.length; i++) { - const orderGoods = goodsList.rows[i]; - const goods = await vk.baseDao.findByWhereJson({ - dbName: "jjx-goods", - whereJson: { - _id: orderGoods.goodsId, - } - }); - if (!goods.sales) { - await vk.baseDao.updateById({ - dbName: "jjx-goods", - id: orderGoods.goodsId, - dataJson: { - sales: 1, - } - }); - } else { - await vk.baseDao.updateById({ - dbName: "jjx-goods", - id: orderGoods.goodsId, - dataJson: { - sales: _.inc(1), - } - }) - } - } - } - if (order.type === 2) { - const goods = await vk.baseDao.findByWhereJson({ - dbName: "jjx-order-goods", - whereJson: { - orderId: order._id, - } - }); - const user = await vk.baseDao.findByWhereJson({ - dbName: "uni-id-users", - whereJson: { - _id: order.uid, - } - }); - if (!user.paidEnsure) { - await vk.baseDao.updateById({ - dbName: "uni-id-users", - id: order.uid, - dataJson: { - paidEnsure: goods.goodsPrice, - } - }); - } else { - await vk.baseDao.updateById({ - dbName: "uni-id-users", - id: order.uid, - dataJson: { - paidEnsure: _.inc(goods.goodsPrice), - } - }); - } - } else if (order.type === 3) { - const goods = await vk.baseDao.findByWhereJson({ - dbName: "jjx-order-goods", - whereJson: { - orderId: order._id, - } - }); - const userLevelConfig = await vk.baseDao.findByWhereJson({ - dbName: "jjx-user-level", - whereJson: { - level: goods.goodsId, - } - }); - let paidEnsure = 0 - if (goods.goodsId === 3) { - paidEnsure = [50000, 100000, 200000][order.ensureIndex] - } - const dataJson = { - userLevel: goods.goodsId, - userGrade: 0, - upgradeExpiredAt: vk.pubfn.getOffsetTime(new Date(), { - year: 1, - mode: "after", - }), - paidEnsure: _.inc(paidEnsure), - } - if (userLevelConfig.ensureAmount && userLevelConfig.ensureAmount > 0) { - dataJson.paidEnsure = _.inc(userLevelConfig.ensureAmount) - } - const user = await vk.baseDao.updateAndReturn({ - dbName: "uni-id-users", - whereJson: { - _id: order.uid, - }, - dataJson - }); - const messageConfig = await vk.baseDao.findByWhereJson({ - dbName: "jjx-message-config", - dataJson: { - uid: order.uid - } - }); + if (order.type === 0) { + const goodsList = await vk.baseDao.select({ + dbName: "jjx-order-goods", + whereJson: { + orderId: order._id, + } + }); + for (let i = 0; i < goodsList.rows.length; i++) { + const orderGoods = goodsList.rows[i]; + const goods = await vk.baseDao.findByWhereJson({ + dbName: "jjx-goods", + whereJson: { + _id: orderGoods.goodsId, + } + }); + if (!goods.sales) { + await vk.baseDao.updateById({ + dbName: "jjx-goods", + id: orderGoods.goodsId, + dataJson: { + sales: 1, + } + }); + } else { + await vk.baseDao.updateById({ + dbName: "jjx-goods", + id: orderGoods.goodsId, + dataJson: { + sales: _.inc(1), + } + }) + } + } + } + if (order.type === 2) { + const goods = await vk.baseDao.findByWhereJson({ + dbName: "jjx-order-goods", + whereJson: { + orderId: order._id, + } + }); + const user = await vk.baseDao.findByWhereJson({ + dbName: "uni-id-users", + whereJson: { + _id: order.uid, + } + }); + if (!user.paidEnsure) { + await vk.baseDao.updateById({ + dbName: "uni-id-users", + id: order.uid, + dataJson: { + paidEnsure: goods.goodsPrice, + } + }); + } else { + await vk.baseDao.updateById({ + dbName: "uni-id-users", + id: order.uid, + dataJson: { + paidEnsure: _.inc(goods.goodsPrice), + } + }); + } + } else if (order.type === 3) { + const goods = await vk.baseDao.findByWhereJson({ + dbName: "jjx-order-goods", + whereJson: { + orderId: order._id, + } + }); + const userLevelConfig = await vk.baseDao.findByWhereJson({ + dbName: "jjx-user-level", + whereJson: { + level: goods.goodsId, + } + }); + let paidEnsure = 0 + if (goods.goodsId === 3) { + paidEnsure = [50000, 100000, 200000][order.ensureIndex] + } + const dataJson = { + userLevel: goods.goodsId, + userGrade: 0, + upgradeExpiredAt: vk.pubfn.getOffsetTime(new Date(), { + year: 1, + mode: "after", + }), + paidEnsure: _.inc(paidEnsure), + } + if (userLevelConfig.ensureAmount && userLevelConfig.ensureAmount > 0) { + dataJson.paidEnsure = _.inc(userLevelConfig.ensureAmount) + } + const user = await vk.baseDao.updateAndReturn({ + dbName: "uni-id-users", + whereJson: { + _id: order.uid, + }, + dataJson + }); + const messageConfig = await vk.baseDao.findByWhereJson({ + dbName: "jjx-message-config", + dataJson: { + uid: order.uid + } + }); - if (!messageConfig) { - await vk.baseDao.add({ - dbName: "jjx-message-config", - dataJson: { - uid: order.uid, - article: true, - process: true, - old: true, - enquiry: true, - productList: [], - addressRegion: {} - } - }) - } - } else if (order.type === 7) { - await vk.baseDao.updateById({ - dbName: "jjx-order", - id: order._id, - dataJson: { - orderStatus: 4 - } - }); + if (!messageConfig) { + await vk.baseDao.add({ + dbName: "jjx-message-config", + dataJson: { + uid: order.uid, + article: true, + process: true, + old: true, + enquiry: true, + productList: [], + addressRegion: {} + } + }) + } + } else if (order.type === 7) { + await vk.baseDao.updateById({ + dbName: "jjx-order", + id: order._id, + dataJson: { + orderStatus: 4 + } + }); - const goods = await vk.baseDao.findByWhereJson({ - dbName: "jjx-order-goods", - whereJson: { - orderId: order._id, - } - }); - // 单次 - if (goods.goodsId === '6587b034466d41a591df881c') { - // 仅针对单条询价次数 - if (goods.isSingleQuote) { - const checkData = await vk.baseDao.findByWhereJson({ - dbName: "jjx-user-quote-num", - whereJson: { - uid: order.uid, - type: goods.type, - sheetId: goods.sheetId, - } - }); - await vk.baseDao.add({ - dbName: "jjx-user-quote-num", - dataJson: { - num: checkData ? _.inc(1) : 1, - type: goods.type, - sheetId: goods.sheetId, - uid: order.uid, - } - }); - } else { - await vk.baseDao.updateById({ - dbName: "uni-id-users", - id: order.uid, - dataJson: { - remainQuoteNum: _.inc(1) - } - }); - } - } else { - const amountItem = await vk.baseDao.findById({ - dbName: 'jjx-quote-num-config', - id: goods.goodsId - }) - const user = await vk.baseDao.findById({ - dbName: "uni-id-users", - id: order.uid - }); - let quoteExpiredAt = vk.pubfn.getOffsetTime(new Date(), { - day: amountItem.day, - mode: "after", - }) - if (user.quoteExpiredAt) { - if (dayjs(user.quoteExpiredAt).isAfter(dayjs())) { - vk.pubfn.getOffsetTime(dayjs(user.quoteExpiredAt).toDate(), { - day: amountItem.day, - mode: "after", - }) - } - } - await vk.baseDao.updateById({ - dbName: "uni-id-users", - id: order.uid, - dataJson: { - quoteExpiredAt - } - }); - } - } else if (order.type === 8) { - await vk.baseDao.updateById({ - dbName: "jjx-order", - id: order._id, - dataJson: { - orderStatus: 4 - } - }); + const goods = await vk.baseDao.findByWhereJson({ + dbName: "jjx-order-goods", + whereJson: { + orderId: order._id, + } + }); + // 单次 + if (goods.goodsId === '6587b034466d41a591df881c') { + // 仅针对单条询价次数 + if (goods.isSingleQuote) { + const checkData = await vk.baseDao.findByWhereJson({ + dbName: "jjx-user-quote-num", + whereJson: { + uid: order.uid, + type: goods.type, + sheetId: goods.sheetId, + } + }); + await vk.baseDao.add({ + dbName: "jjx-user-quote-num", + dataJson: { + num: checkData ? _.inc(1) : 1, + type: goods.type, + sheetId: goods.sheetId, + uid: order.uid, + } + }); + } else { + await vk.baseDao.updateById({ + dbName: "uni-id-users", + id: order.uid, + dataJson: { + remainQuoteNum: _.inc(1) + } + }); + } + } else { + const amountItem = await vk.baseDao.findById({ + dbName: 'jjx-quote-num-config', + id: goods.goodsId + }) + const user = await vk.baseDao.findById({ + dbName: "uni-id-users", + id: order.uid + }); + let quoteExpiredAt = vk.pubfn.getOffsetTime(new Date(), { + day: amountItem.day, + mode: "after", + }) + if (user.quoteExpiredAt) { + if (dayjs(user.quoteExpiredAt).isAfter(dayjs())) { + vk.pubfn.getOffsetTime(dayjs(user.quoteExpiredAt).toDate(), { + day: amountItem.day, + mode: "after", + }) + } + } + await vk.baseDao.updateById({ + dbName: "uni-id-users", + id: order.uid, + dataJson: { + quoteExpiredAt + } + }); + } + } else if (order.type === 8) { + await vk.baseDao.updateById({ + dbName: "jjx-order", + id: order._id, + dataJson: { + orderStatus: 4 + } + }); - const goods = await vk.baseDao.findByWhereJson({ - dbName: "jjx-order-goods", - whereJson: { - orderId: order._id, - } - }); + const goods = await vk.baseDao.findByWhereJson({ + dbName: "jjx-order-goods", + whereJson: { + orderId: order._id, + } + }); - const hasData = await vk.baseDao.findByWhereJson({ - dbName: "jjx-spot-ad", - whereJson: { - uid: order.uid, - province: goods.goodsId - } - }) + const hasData = await vk.baseDao.findByWhereJson({ + dbName: "jjx-spot-ad", + whereJson: { + uid: order.uid, + province: goods.goodsId + } + }) - let topExpiredAt = vk.pubfn.getOffsetTime(new Date(), { - month: goods.num, - mode: "after", - }) + let topExpiredAt = vk.pubfn.getOffsetTime(new Date(), { + month: goods.num, + mode: "after", + }) - if (!hasData) { - await vk.baseDao.add({ - dbName: "jjx-spot-ad", - dataJson: { - uid: order.uid, - province: goods.goodsId, - topExpiredAt - } - }) - } else { - topExpiredAt = vk.pubfn.getOffsetTime(hasData.topExpiredAt, { - month: goods.num, - mode: "after", - }) - await vk.baseDao.updateById({ - dbName: "jjx-spot-ad", - id: hasData._id, - dataJson: { - topExpiredAt - } - }) - } + if (!hasData) { + await vk.baseDao.add({ + dbName: "jjx-spot-ad", + dataJson: { + uid: order.uid, + province: goods.goodsId, + topExpiredAt + } + }) + } else { + topExpiredAt = vk.pubfn.getOffsetTime(hasData.topExpiredAt, { + month: goods.num, + mode: "after", + }) + await vk.baseDao.updateById({ + dbName: "jjx-spot-ad", + id: hasData._id, + dataJson: { + topExpiredAt + } + }) + } - await vk.baseDao.updateById({ - dbName: "jjx-spot-goods", - id: goods.pk, - dataJson: { - topExpiredAt - } - }); - } else if (order.type === 9) { - await vk.baseDao.updateById({ - dbName: "jjx-order", - id: order._id, - dataJson: { - orderStatus: 4 - } - }); + await vk.baseDao.updateById({ + dbName: "jjx-spot-goods", + id: goods.pk, + dataJson: { + topExpiredAt + } + }); + } else if (order.type === 9) { + await vk.baseDao.updateById({ + dbName: "jjx-order", + id: order._id, + dataJson: { + orderStatus: 4 + } + }); - const goods = await vk.baseDao.findByWhereJson({ - dbName: "jjx-order-goods", - whereJson: { - orderId: order._id, - } - }); + const goods = await vk.baseDao.findByWhereJson({ + dbName: "jjx-order-goods", + whereJson: { + orderId: order._id, + } + }); - await vk.baseDao.updateById({ - dbName: "jjx-old", - id: goods.goodsId, - dataJson: { - isUrgent: true - } - }) - } else if (order.type === 10) { - await vk.baseDao.updateById({ - dbName: "jjx-order", - id: order._id, - dataJson: { - orderStatus: 4 - } - }); + await vk.baseDao.updateById({ + dbName: "jjx-old", + id: goods.goodsId, + dataJson: { + isUrgent: true + } + }) + } else if (order.type === 10) { + await vk.baseDao.updateById({ + dbName: "jjx-order", + id: order._id, + dataJson: { + orderStatus: 4 + } + }); - await vk.baseDao.updateById({ - dbName: "uni-id-users", - id: order.uid, - dataJson: { - remainOldNum: _.inc(1) - } - }); - } else if (order.type === 11) { - await vk.baseDao.updateById({ - dbName: "uni-id-users", - id: order.uid, - dataJson: { - remainArticleDownloadNum: _.inc(1) - } - }); - } else if (order.type === 12) { - const goods = await vk.baseDao.findByWhereJson({ - dbName: "jjx-order-goods", - whereJson: { - orderId: order._id, - } - }); - const contract = await vk.baseDao.findById({ - dbName: "jjx-contract", - id: goods.goodsId - }); - const canDownloadUidList = [] - if (!contract.canDownloadUidList) { - canDownloadUidList.push(order.uid) - } else { - canDownloadUidList.push(...contract.canDownloadUidList, order.uid) - } - await vk.baseDao.updateById({ - dbName: "jjx-contract", - id: goods.goodsId, - dataJson: { - canDownloadUidList - } - }); - await vk.baseDao.update({ - dbName: "jjx-certificate", - whereJson: { - contractId: goods.goodsId - }, - dataJson: { - status: 1, - paidConfirm: true - } - }); + await vk.baseDao.updateById({ + dbName: "uni-id-users", + id: order.uid, + dataJson: { + remainOldNum: _.inc(1) + } + }); + } else if (order.type === 11) { + await vk.baseDao.updateById({ + dbName: "uni-id-users", + id: order.uid, + dataJson: { + remainArticleDownloadNum: _.inc(1) + } + }); + } else if (order.type === 12) { + const goods = await vk.baseDao.findByWhereJson({ + dbName: "jjx-order-goods", + whereJson: { + orderId: order._id, + } + }); + const contract = await vk.baseDao.findById({ + dbName: "jjx-contract", + id: goods.goodsId + }); + const canDownloadUidList = [] + if (!contract.canDownloadUidList) { + canDownloadUidList.push(order.uid) + } else { + canDownloadUidList.push(...contract.canDownloadUidList, order.uid) + } + await vk.baseDao.updateById({ + dbName: "jjx-contract", + id: goods.goodsId, + dataJson: { + canDownloadUidList + } + }); + await vk.baseDao.update({ + dbName: "jjx-certificate", + whereJson: { + contractId: goods.goodsId + }, + dataJson: { + status: 1, + paidConfirm: true + } + }); - const certificate = await vk.baseDao.findByWhereJson({ - dbName: "jjx-certificate", - whereJson: { - contractId: goods.goodsId, - uid: order.uid - } - }); - if (certificate.invoiceTypes !== 'none') { - await vk.baseDao.updateById({ - dbName: "jjx-order", - id: order._id, - dataJson: { - isInvoice: 1, - name: certificate.companyName, - code: certificate.taxNumber, - phone: certificate.phone, - } - }); - } + const certificate = await vk.baseDao.findByWhereJson({ + dbName: "jjx-certificate", + whereJson: { + contractId: goods.goodsId, + uid: order.uid + } + }); + if (certificate.invoiceTypes !== 'none') { + await vk.baseDao.updateById({ + dbName: "jjx-order", + id: order._id, + dataJson: { + isInvoice: 1, + name: certificate.companyName, + code: certificate.taxNumber, + phone: certificate.phone, + } + }); + } - } else if (order.type === 13) { - const goods = await vk.baseDao.findByWhereJson({ - dbName: "jjx-order-goods", - whereJson: { - orderId: order._id, - } - }); - const qyb = await vk.baseDao.findByWhereJson({ - dbName: "jjx-qyb", - whereJson: { - _id: goods.goodsId - } - }); - const user = await vk.baseDao.findByWhereJson({ - dbName: "uni-id-users", - whereJson: { - _id: order.uid, - } - }); - const remainNum = user.qybNum + qyb.getNum; - await vk.baseDao.updateById({ - dbName: "uni-id-users", - id: order.uid, - dataJson: { - qybNum: _.inc(qyb.getNum) - } - }); - await vk.baseDao.updateById({ - dbName: "jjx-order", - id: order._id, - dataJson: { - remainNum - } - }); - } - } - if (!order) { - console.log('order not found') - order = await vk.baseDao.findByWhereJson({ - dbName: "jjx-order-pay-list", - whereJson: { - orderNo - } - }); - console.log('pay list order data', order) - if (order && !order.sellerConfirm && (data.billStatus === 'PAID' || data.status === 'TRADE_SUCCESS')) { - await vk.baseDao.updateById({ - dbName: "jjx-order-pay-list", - id: order._id, - dataJson: { - // sellerConfirm: true, - hasPaid: true, - paidAt: vk.pubfn.timeFormat(new Date(), "yyyy-MM-dd hh:mm:ss"), - // confirmAt: vk.pubfn.timeFormat(new Date(), "yyyy-MM-dd hh:mm:ss"), - } - }); - } - } - // 业务逻辑结束----------------------------------------------------------- - return res; - }, - /** - * 模板函数 - * @url client/payment.test 前端调用的url参数地址 - */ - test: async function (data) { - let res = {code: 0, msg: ''}; - let {uid} = this.getClientInfo(); // 获取客户端信息 - // 业务逻辑开始----------------------------------------------------------- + } else if (order.type === 13) { + const goods = await vk.baseDao.findByWhereJson({ + dbName: "jjx-order-goods", + whereJson: { + orderId: order._id, + } + }); + const qyb = await vk.baseDao.findByWhereJson({ + dbName: "jjx-qyb", + whereJson: { + _id: goods.goodsId + } + }); + const user = await vk.baseDao.findByWhereJson({ + dbName: "uni-id-users", + whereJson: { + _id: order.uid, + } + }); + const remainNum = user.qybNum + qyb.getNum; + await vk.baseDao.updateById({ + dbName: "uni-id-users", + id: order.uid, + dataJson: { + qybNum: _.inc(qyb.getNum) + } + }); + await vk.baseDao.updateById({ + dbName: "jjx-order", + id: order._id, + dataJson: { + remainNum + } + }); + } else if (order.type === 20) { + // 积分充值到账 + const goods = await vk.baseDao.findByWhereJson({ + dbName: "jjx-order-goods", + whereJson: { + orderId: order._id + } + }); + if (goods) { + const points = parseInt(goods.goodsName.replace('积分充值_', '').replace('积分', '')); + if (points > 0) { + // 增加积分余额 + const userPoints = await vk.baseDao.findByWhereJson({ + dbName: "jjx-user-points", + whereJson: { + _id: order.uid + } + }); + if (userPoints) { + await vk.baseDao.updateById({ + dbName: "jjx-user-points", + id: order.uid, + dataJson: { + balance: _.inc(points), + total_earned: _.inc(points), + update_time: new Date() + } + }); + } else { + await vk.baseDao.add({ + dbName: "jjx-user-points", + dataJson: { + _id: order.uid, + balance: points, + total_earned: points, + total_spent: 0, + gift: 0, + update_time: new Date() + } + }); + } + // 记录流水 + const updatedPoints = await vk.baseDao.findById({ + dbName: "jjx-user-points", + id: order.uid + }); + await vk.baseDao.add({ + dbName: "jjx-points-log", + dataJson: { + user_id: order.uid, + type: 'earn', + amount: points, + balance_after: updatedPoints ? updatedPoints.balance : points, + reason: '积分充值(' + order.offAmount + '元)', + biz_type: 'recharge', + biz_id: order._id, + _add_time: new Date() + } + }); + } + } + await vk.baseDao.updateById({ + dbName: "jjx-order", + id: order._id, + dataJson: { + orderStatus: 4 + } + }); + } + } + if (!order) { + console.log('order not found') + order = await vk.baseDao.findByWhereJson({ + dbName: "jjx-order-pay-list", + whereJson: { + orderNo + } + }); + console.log('pay list order data', order) + if (order && !order.sellerConfirm && (data.billStatus === 'PAID' || data.status === + 'TRADE_SUCCESS')) { + await vk.baseDao.updateById({ + dbName: "jjx-order-pay-list", + id: order._id, + dataJson: { + // sellerConfirm: true, + hasPaid: true, + paidAt: vk.pubfn.timeFormat(new Date(), "yyyy-MM-dd hh:mm:ss"), + // confirmAt: vk.pubfn.timeFormat(new Date(), "yyyy-MM-dd hh:mm:ss"), + } + }); + } + } + // 业务逻辑结束----------------------------------------------------------- + return res; + }, + /** + * 模板函数 + * @url client/payment.test 前端调用的url参数地址 + */ + test: async function(data) { + let res = { + code: 0, + msg: '' + }; + let { + uid + } = this.getClientInfo(); // 获取客户端信息 + // 业务逻辑开始----------------------------------------------------------- - // 业务逻辑结束----------------------------------------------------------- - return res; - }, + // 业务逻辑结束----------------------------------------------------------- + return res; + }, }; -module.exports = cloudObject; +module.exports = cloudObject; \ No newline at end of file diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/createOrder.js b/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/createOrder.js new file mode 100644 index 0000000..576481a --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/createOrder.js @@ -0,0 +1,39 @@ +//创建积分充值订单 +module.exports = { + main: async (event) => { + let { data = {}, userInfo, util } = event; + let { vk, db, _ } = util; + const { points, price } = data; // points=积分数量 price=价格(分→元) + + const amount = price / 100; // 分转元 + const offAmount = amount; + const orderId = await vk.baseDao.add({ + dbName: "jjx-order", + dataJson: { + orderNo: vk.pubfn.createOrderNo("39XU", 20), + uid: userInfo.uid, + orderStatus: 0, + type: 20, // 20=积分充值 + amount, + offAmount, + saveAmount: 0, + offRate: 100, + } + }); + + await vk.baseDao.add({ + dbName: "jjx-order-goods", + dataJson: { + orderId, + goodsId: 'points_recharge', + num: 1, + goodsName: `积分充值_${points}积分`, + goodsPrice: amount, + goodsCover: null, + } + }); + + return { code: 0, msg: 'ok', data: orderId }; + } +}; + diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/deduct.js b/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/deduct.js new file mode 100644 index 0000000..d91c361 --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/deduct.js @@ -0,0 +1,47 @@ +//扣积分(合同审查用) +module.exports = { + main: async (event) => { + let { data = {}, userInfo, util } = event; + let { db, _ } = util; + const { amount, reason, biz_type, biz_id } = data; + + if (!amount || amount <= 0) { + return { code: -1, msg: '扣减数量无效' }; + } + + // 查余额 + const userRes = await db.collection('jjx-user-points').doc(userInfo.uid).get(); + if (!userRes.data.length) { + return { code: -1, msg: '积分账户不存在,请先获取积分' }; + } + + const currentBalance = userRes.data[0].balance; + if (currentBalance < amount) { + return { code: -1, msg: '积分不足', data: { balance: currentBalance, needed: amount } }; + } + + const newBalance = currentBalance - amount; + + // 扣减余额 + await db.collection('jjx-user-points').doc(userInfo.uid).update({ + balance: newBalance, + total_spent: _.inc(amount), + update_time: new Date() + }); + + // 记录流水 + await db.collection('jjx-points-log').add({ + user_id: userInfo.uid, + type: 'spend', + amount: -amount, + balance_after: newBalance, + reason: reason || '积分消耗', + biz_type: biz_type || 'other', + biz_id: biz_id || '', + _add_time: new Date() + }); + + return { code: 0, msg: 'ok', data: { balance: newBalance } }; + } +}; + diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/getBalance.js b/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/getBalance.js new file mode 100644 index 0000000..7d39a54 --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/getBalance.js @@ -0,0 +1,37 @@ +// 获取积分余额 +module.exports = { + main: async (event) => { + let { data = {}, userInfo, util } = event; + let { db, _ } = util; + //默认积分500 + let amount = 500; + + const res = await db.collection('jjx-user-points').doc(userInfo.uid).get(); + + if (res.data.length === 0) { + // 新用户,自动赠送 500 积分 + await db.collection('jjx-user-points').add({ + _id: userInfo.uid, + balance: amount, + total_earned: amount, + total_spent: 0, + gift: amount, + update_time: new Date() + }); + + await db.collection('jjx-points-log').add({ + user_id: userInfo.uid, + type: 'earn', + amount: amount, + balance_after: amount, + reason: '新用户注册赠送', + biz_type: 'gift', + _add_time: new Date() + }); + + return { code: 0, msg: 'ok', data: { balance: amount, total_earned: amount, total_spent: 0, gift: amount } }; + } + + return { code: 0, msg: 'ok', data: res.data[0] }; + } +}; diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/getLog.js b/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/getLog.js new file mode 100644 index 0000000..b9d7e61 --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/getLog.js @@ -0,0 +1,40 @@ +//查询积分流水 +module.exports = { + main: async (event) => { + let { data = {}, userInfo, util } = event; + let { vk, db, _ } = util; + const { pageIndex = 1, pageSize = 50, biz_type } = data; + + console.log('getLog uid:', userInfo.uid); + + // 先查全部看看有没有数据 + const allRes = await db.collection('jjx-points-log').limit(5).get(); + console.log('getLog all data count:', allRes.data.length); + if (allRes.data.length > 0) { + console.log('getLog first record:', JSON.stringify(allRes.data[0])); + } + + const where = { user_id: userInfo.uid }; + if (biz_type) where.biz_type = biz_type; + + const countRes = await db.collection('jjx-points-log').where(where).count(); + console.log('getLog filtered count:', countRes.total); + + const listRes = await db.collection('jjx-points-log') + .where(where) + .orderBy('_add_time', 'desc') + .skip((pageIndex - 1) * pageSize) + .limit(pageSize) + .get(); + + return { + code: 0, + msg: 'ok', + data: { + total: countRes.total, + rows: listRes.data + } + }; + } +}; + diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/recharge.js b/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/recharge.js new file mode 100644 index 0000000..0a10527 --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/router/service/client/points/kh/recharge.js @@ -0,0 +1,55 @@ +//积分充值 +module.exports = { + main: async (event) => { + let { data = {}, userInfo, util } = event; + let { db, _ } = util; + const { order_id } = data; + + if (!order_id) { + return { code: -1, msg: '缺少订单ID' }; + } + + // 查订单 + const orderRes = await db.collection('vk-pay-orders').doc(order_id).get(); + if (!orderRes.data.length) { + return { code: -1, msg: '订单不存在' }; + } + + const order = orderRes.data[0]; + if (order.status !== 1) { + return { code: -1, msg: '订单未支付' }; + } + + // 解析充值积分数量 + const attachedData = JSON.parse(order.attached_data || '{}'); + const points = attachedData.points; + if (!points) { + return { code: -1, msg: '订单数据异常' }; + } + + // 增加余额 + await db.collection('jjx-user-points').doc(userInfo.uid).update({ + balance: _.inc(points), + total_earned: _.inc(points), + update_time: new Date() + }); + + // 查最新余额 + const userRes = await db.collection('jjx-user-points').doc(userInfo.uid).get(); + const balance = userRes.data[0].balance; + + // 记录流水 + await db.collection('jjx-points-log').add({ + user_id: userInfo.uid, + type: 'earn', + amount: points, + balance_after: balance, + reason: '积分充值(' + (order.total_fee / 100) + '元)', + biz_type: 'recharge', + biz_id: order_id, + _add_time: new Date() + }); + + return { code: 0, msg: 'ok', data: { balance, points_added: points } }; + } +}; diff --git a/uniCloud-tcb/cloudfunctions/router/service/common/公共逻辑(公共接口).md b/uniCloud-tcb/cloudfunctions/router/service/common/公共逻辑(公共接口).md new file mode 100644 index 0000000..e69de29 diff --git a/uniCloud-tcb/cloudfunctions/router/service/template/test/sys/test.js b/uniCloud-tcb/cloudfunctions/router/service/template/test/sys/test.js new file mode 100644 index 0000000..0fec407 --- /dev/null +++ b/uniCloud-tcb/cloudfunctions/router/service/template/test/sys/test.js @@ -0,0 +1,20 @@ +module.exports = { + /** + * 此函数名称 + * @url template/test/sys/test 前端调用的url参数地址 + * data 请求参数 说明 + * res 返回参数说明 + * @params {Number} code 错误码,0表示成功 + * @params {String} msg 详细信息 + */ + main: async (event) => { + let { data = {}, userInfo, util, originalParam } = event; + let { uniID, config, pubFun, vk , db, _ } = util; + let { uid } = data; + let res = { code : 0, msg : '' }; + // 业务逻辑开始----------------------------------------------------------- + res.msg = "ok"; + // 业务逻辑结束----------------------------------------------------------- + return res; + } +} diff --git a/uniCloud-tcb/database/jjx-points-log.schema.json b/uniCloud-tcb/database/jjx-points-log.schema.json new file mode 100644 index 0000000..20e7ebd --- /dev/null +++ b/uniCloud-tcb/database/jjx-points-log.schema.json @@ -0,0 +1 @@ +{"bsonType":"object","required":[],"properties":{"_id":{"description":"记录ID,系统自动生成","bsonType":"string"},"user_id":{"bsonType":"string","title":"用户ID","description":"关联 uni-id-users._id","foreignKey":"uni-id-users._id"},"type":{"bsonType":"string","title":"变动类型","description":"earn=获得 spend=消耗","enum":[{"text":"获得","value":"earn"},{"text":"消耗","value":"spend"}]},"amount":{"bsonType":"number","title":"变动数量","description":"正数为获得,负数为消耗"},"balance_after":{"bsonType":"number","title":"变动后余额","description":"本次变动后的积分余额"},"reason":{"bsonType":"string","title":"变动原因","description":"如:新用户赠送、积分充值、合同审查消耗","trim":"both"},"biz_type":{"bsonType":"string","title":"业务类型","description":"关联的业务类型","enum":[{"text":"赠送","value":"gift"},{"text":"充值","value":"recharge"},{"text":"合同审查","value":"contract_review"},{"text":"签到","value":"checkin"},{"text":"其他","value":"other"}]},"biz_id":{"bsonType":"string","title":"业务ID","description":"关联的业务记录ID(如订单ID)"},"_add_time":{"bsonType":"date","title":"创建时间","description":"记录创建时间,系统自动填充"}}} \ No newline at end of file diff --git a/uniCloud-tcb/database/jjx-review-records.schema.json b/uniCloud-tcb/database/jjx-review-records.schema.json new file mode 100644 index 0000000..6bbfb24 --- /dev/null +++ b/uniCloud-tcb/database/jjx-review-records.schema.json @@ -0,0 +1 @@ +{"bsonType":"object","required":[],"properties":{"_id":{"description":"记录ID,系统自动生成","bsonType":"string"},"user_id":{"bsonType":"string","title":"用户ID","description":"关联 uni-id-users._id","foreignKey":"uni-id-users._id"},"file_name":{"bsonType":"string","title":"文件名","description":"上传的合同文件名","trim":"both"},"file_size":{"bsonType":"number","title":"文件大小","description":"文件大小(字节)"},"file_id":{"bsonType":"string","title":"扣子文件ID","description":"上传到扣子后的 file_id"},"file_url":{"bsonType":"string","title":"文件预览地址","description":"uniCloud 云存储临时链接,用于查看\/下载原文件"},"html_url":{"bsonType":"string","title":"HTML报告地址","description":"精美 HTML 审查报告链接"},"pdf_url":{"bsonType":"string","title":"PDF报告地址","description":"PDF 审查报告下载链接"},"contract_info":{"bsonType":"object","title":"合同结构化信息","description":"AI 返回的合同名称、风险等级、风险统计等"},"chat_id":{"bsonType":"string","title":"对话ID","description":"扣子对话 chat_id"},"conversation_id":{"bsonType":"string","title":"会话ID","description":"扣子会话 conversation_id"},"status":{"bsonType":"string","title":"审查状态","description":"uploaded=待审查 reviewing=等待处理 processing=审查中 completed=已完成 failed=失败","enum":[{"text":"待审查","value":"uploaded"},{"text":"等待处理","value":"reviewing"},{"text":"审查中","value":"processing"},{"text":"已完成","value":"completed"},{"text":"失败","value":"failed"}]},"result":{"bsonType":"string","title":"审查结果","description":"AI 返回的审查结果内容"},"points_used":{"bsonType":"number","title":"消耗积分","description":"本次审查消耗的积分数量(token_count \/ 100)"},"token_count":{"bsonType":"number","title":"Token消耗","description":"扣子返回的 token 总消耗"},"_add_time":{"bsonType":"date","title":"创建时间","description":"记录创建时间"},"update_time":{"bsonType":"date","title":"更新时间","description":"最后更新时间"}}} \ No newline at end of file diff --git a/uniCloud-tcb/database/jjx-user-points.schema.json b/uniCloud-tcb/database/jjx-user-points.schema.json new file mode 100644 index 0000000..5d77842 --- /dev/null +++ b/uniCloud-tcb/database/jjx-user-points.schema.json @@ -0,0 +1 @@ +{"bsonType":"object","required":[],"properties":{"_id":{"description":"用户ID(与 uni-id-users._id 对应)","bsonType":"string"},"balance":{"bsonType":"number","title":"积分余额","description":"当前可用积分","defaultValue":0,"minimum":0},"total_earned":{"bsonType":"number","title":"累计获得","description":"累计获得的积分总数","defaultValue":0},"total_spent":{"bsonType":"number","title":"累计消耗","description":"累计消耗的积分总数","defaultValue":0},"gift":{"bsonType":"number","title":"赠送积分","description":"新用户赠送的积分数量","defaultValue":500},"update_time":{"bsonType":"date","title":"更新时间","description":"最后更新时间"}}} \ No newline at end of file