chengjie 4 月之前
父节点
当前提交
6b103ee459

+ 132 - 0
src/api/yjbdc/queuedArticleController.js

@@ -0,0 +1,132 @@
1
+import { GenerateArticle } from './yjbdcController.js';
2
+import requestQueue, { userRequestMap } from './requestQueue.js';
3
+import { globalCache } from '../../util/GlobalCache.js';
4
+
5
+// 启动队列处理器
6
+requestQueue.startQueueProcessor(GenerateArticle);
7
+
8
+/**
9
+ * 队列化文章生成API
10
+ * 如果当前一分钟内的处理队列少于上限,则直接调用GenerateArticle同步生成
11
+ * 如果当前队列大于等于上限,则告知用户排队中,返回result:-2
12
+ * 对于同一个用户ID,在一分钟内,如果超过1次请求,直接返回-1,不处理请求
13
+ */
14
+export async function QueuedGenerateArticle(ctx) {
15
+  try {
16
+    // 检查请求参数
17
+    if (!ctx.query || !ctx.query.UserID) {
18
+      ctx.body = { errcode: 10001, result: "-1", message: "缺少必要参数 UserID" };
19
+      return;
20
+    }
21
+    
22
+    const userID = ctx.query.UserID;
23
+    
24
+    // 检查用户请求频率限制(一分钟内只允许一次请求)
25
+    if (!requestQueue.canUserRequest(userID)) {
26
+      console.log(`用户 ${userID} 请求频率过高,一分钟内只允许一次请求`);
27
+      ctx.body = { 
28
+        errcode: 10000, 
29
+        result: "-1", 
30
+        message: "请求频率过高,一分钟内只允许一次请求" 
31
+      };
32
+      return;
33
+    }
34
+    
35
+    // 检查是否可以立即处理请求
36
+    if (requestQueue.canProcessImmediately()) {
37
+      // 直接调用原始的GenerateArticle函数
38
+      console.log(`直接处理请求,无需排队,用户ID: ${userID}`);
39
+      await GenerateArticle(ctx);
40
+    } else {
41
+      // 获取当前队列状态
42
+      const queueStatus = requestQueue.getQueueStatus();
43
+      console.log(`请求加入队列,当前队列长度: ${queueStatus.queueLength},用户ID: ${userID}`);
44
+      
45
+      // 将请求添加到队列
46
+      const result = requestQueue.addToQueue(ctx);
47
+      ctx.body = result;
48
+    }
49
+  } catch (error) {
50
+    console.error(`处理队列化请求时出错,用户ID: ${ctx.query?.UserID || 'unknown'}:`, error);
51
+    ctx.body = { errcode: 10002, result: "-1", message: "处理请求时出错" };
52
+  }
53
+}
54
+
55
+/**
56
+ * 获取队列状态信息
57
+ */
58
+export async function GetQueueStatus(ctx) {
59
+  ctx.body = {
60
+    errcode: 10000,
61
+    result: requestQueue.getQueueStatus()
62
+  };
63
+}
64
+
65
+/**
66
+ * 获取特定用户的请求状态
67
+ */
68
+export async function GetUserRequestStatus(ctx) {
69
+  try {
70
+    const userID = ctx.query.UserID;
71
+    
72
+    if (!userID) {
73
+      ctx.body = { errcode: 10001, result: "-1", message: "缺少必要参数 UserID" };
74
+      return;
75
+    }
76
+    
77
+    // 从缓存中获取用户请求状态
78
+    const url = `GenerateArticle?UserID=${userID}`;
79
+    const status = globalCache.get(url);
80
+    
81
+    // 检查用户是否在队列中
82
+    const queuePosition = requestQueue.findUserPosition(userID);
83
+    
84
+    // 检查用户请求频率限制状态
85
+    const now = Date.now();
86
+    const lastRequestTime = userRequestMap.get(userID);
87
+    let canRequestAgain = true;
88
+    let timeUntilNextRequest = 0;
89
+    
90
+    if (lastRequestTime) {
91
+      const elapsedTime = now - lastRequestTime;
92
+      if (elapsedTime < 60000) {
93
+        canRequestAgain = false;
94
+        timeUntilNextRequest = Math.ceil((60000 - elapsedTime) / 1000);
95
+      }
96
+    }
97
+    
98
+    ctx.body = {
99
+      errcode: 10000,
100
+      result: {
101
+        status: status === undefined ? "未找到请求" : status,
102
+        inQueue: queuePosition > -1,
103
+        queuePosition: queuePosition,
104
+        estimatedTimeSeconds: queuePosition > 0 ? queuePosition * 60 / requestQueue.QUEUE_CONFIG.MAX_REQUESTS_PER_MINUTE : 0,
105
+        canRequestAgain: canRequestAgain,
106
+        timeUntilNextRequest: timeUntilNextRequest // 秒数
107
+      }
108
+    };
109
+  } catch (error) {
110
+    console.error(`获取用户请求状态时出错,用户ID: ${ctx.query?.UserID || 'unknown'}:`, error);
111
+    ctx.body = { errcode: 10002, result: "-1", message: "获取请求状态时出错" };
112
+  }
113
+}
114
+
115
+/**
116
+ * 更新队列配置
117
+ */
118
+export async function UpdateQueueConfig(ctx) {
119
+  const params = ctx.request.body;
120
+  requestQueue.updateConfig({
121
+    maxRequestsPerMinute: params.maxRequestsPerMinute,
122
+    queueCheckInterval: params.queueCheckInterval
123
+  });
124
+  
125
+  ctx.body = {
126
+    errcode: 10000,
127
+    result: {
128
+      message: '队列配置已更新',
129
+      currentConfig: requestQueue.QUEUE_CONFIG
130
+    }
131
+  };
132
+}

+ 238 - 0
src/api/yjbdc/requestQueue.js

@@ -0,0 +1,238 @@
1
+// 请求队列管理模块
2
+import { globalCache } from '../../util/GlobalCache.js';
3
+import config from '../../config/index.js';
4
+
5
+// 导出用户请求映射,供其他模块使用
6
+export const userRequestMap = new Map();
7
+
8
+// 队列配置
9
+const QUEUE_CONFIG = {
10
+  MAX_REQUESTS_PER_MINUTE: 50, // 每分钟最大请求数,可配置
11
+  QUEUE_CHECK_INTERVAL: 1000, // 队列检查间隔(毫秒)
12
+};
13
+
14
+// 请求队列
15
+const requestQueue = [];
16
+
17
+// 追踪当前分钟内已处理的请求数
18
+let requestsInCurrentMinute = 0;
19
+let lastMinuteReset = Date.now();
20
+
21
+// 用户请求记录映射,记录用户最近一次请求的时间戳
22
+// 已在上方导出,此处不再重复定义
23
+
24
+// 重置计数器
25
+function resetRequestCounter() {
26
+  const now = Date.now();
27
+  if (now - lastMinuteReset >= 60000) {
28
+    requestsInCurrentMinute = 0;
29
+    lastMinuteReset = now;
30
+    return true;
31
+  }
32
+  return false;
33
+}
34
+
35
+// 处理队列中的请求
36
+async function processQueue(generateArticleFunc) {
37
+  if (requestQueue.length === 0) return;
38
+  
39
+  // 检查是否需要重置计数器
40
+  resetRequestCounter();
41
+  
42
+  // 如果当前分钟内的请求数已达到上限,则等待下一次检查
43
+  if (requestsInCurrentMinute >= QUEUE_CONFIG.MAX_REQUESTS_PER_MINUTE) {
44
+    return;
45
+  }
46
+  
47
+  // 从队列中取出一个请求
48
+  const request = requestQueue.shift();
49
+  if (!request) return;
50
+  
51
+  // 增加当前分钟内的请求计数
52
+  requestsInCurrentMinute++;
53
+  
54
+  // 计算请求在队列中等待的时间
55
+  const waitTime = Date.now() - request.enqueuedAt;
56
+  console.log(`开始处理队列请求,用户ID: ${request.userID},等待时间: ${waitTime}ms`);
57
+  
58
+  // 构建一个模拟的 ctx 对象,用于传递给 GenerateArticle 函数
59
+  const mockCtx = {
60
+    query: request.ctx.query,
61
+    request: request.ctx.request,
62
+    body: undefined, // 将由 GenerateArticle 函数设置
63
+    set: () => {}, // 空函数,因为我们不需要设置 HTTP 头
64
+    // 添加其他可能需要的属性...
65
+  };
66
+  
67
+  try {
68
+    // 执行生成文章的函数 - GenerateArticle 会自行设置缓存状态
69
+    const url = `GenerateArticle?UserID=${request.userID}`;
70
+    
71
+    // 执行生成文章的函数
72
+    await generateArticleFunc(mockCtx);
73
+    
74
+    // GenerateArticle 函数会自行处理缓存,所以这里不需要额外操作
75
+    console.log(`队列请求处理完成: ${url},处理时间: ${Date.now() - request.enqueuedAt}ms`);
76
+  } catch (error) {
77
+    console.error(`队列处理请求时出错,用户ID: ${request.userID}:`, error);
78
+    
79
+    // 出错时设置错误状态到缓存
80
+    const url = `GenerateArticle?UserID=${request.userID}`;
81
+    const errorResult = { errcode: 10000, result: "-1", error: "处理请求时出错" };
82
+    globalCache.set(url, errorResult, config.BufferMemoryTime);
83
+  }
84
+}
85
+
86
+// 启动队列处理器
87
+let queueProcessorInterval = null;
88
+
89
+function startQueueProcessor(generateArticleFunc) {
90
+  if (queueProcessorInterval) return;
91
+  
92
+  // 保存函数引用以便重启时使用
93
+  generateArticleFuncRef = generateArticleFunc;
94
+  
95
+  queueProcessorInterval = setInterval(() => {
96
+    processQueue(generateArticleFunc);
97
+  }, QUEUE_CONFIG.QUEUE_CHECK_INTERVAL);
98
+  
99
+  console.log('队列处理器已启动');
100
+}
101
+
102
+// 停止队列处理器
103
+function stopQueueProcessor() {
104
+  if (queueProcessorInterval) {
105
+    clearInterval(queueProcessorInterval);
106
+    queueProcessorInterval = null;
107
+    console.log('队列处理器已停止');
108
+  }
109
+}
110
+
111
+// 添加请求到队列
112
+function addToQueue(ctx) {
113
+  // 深拷贝 ctx 对象的必要属性,避免引用问题
114
+  const ctxCopy = {
115
+    query: { ...ctx.query },
116
+    request: {
117
+      body: ctx.request.body ? JSON.parse(JSON.stringify(ctx.request.body)) : {}
118
+    },
119
+    // 添加一个响应处理函数,用于设置队列处理结果
120
+    setBody: (body) => {
121
+      // 这个函数在队列处理时不会被调用,仅作为占位符
122
+      console.log('队列处理完成,但无法直接返回结果给客户端');
123
+    }
124
+  };
125
+  
126
+  // 设置请求加入队列的时间戳,用于监控队列延迟
127
+  const queueItem = {
128
+    ctx: ctxCopy,
129
+    enqueuedAt: Date.now(),
130
+    userID: ctx.query.UserID
131
+  };
132
+  
133
+  requestQueue.push(queueItem);
134
+  console.log(`请求已添加到队列,当前队列长度: ${requestQueue.length},用户ID: ${ctx.query.UserID}`);
135
+  
136
+  // 在缓存中标记该请求正在队列中处理
137
+  const url = `GenerateArticle?UserID=${ctx.query.UserID}`;
138
+  const queueStatus = { errcode: 10000, result: "-2", queuePosition: requestQueue.length, isQueued: true };
139
+  globalCache.set(url, queueStatus, config.BufferMemoryTime);
140
+  
141
+  return queueStatus; // 返回排队中的状态码和队列位置
142
+}
143
+
144
+// 检查是否可以立即处理请求
145
+function canProcessImmediately() {
146
+  resetRequestCounter();
147
+  return requestsInCurrentMinute < QUEUE_CONFIG.MAX_REQUESTS_PER_MINUTE;
148
+}
149
+
150
+// 获取队列状态信息
151
+function getQueueStatus() {
152
+  return {
153
+    queueLength: requestQueue.length,
154
+    requestsInCurrentMinute,
155
+    maxRequestsPerMinute: QUEUE_CONFIG.MAX_REQUESTS_PER_MINUTE,
156
+    timeUntilReset: Math.max(0, 60000 - (Date.now() - lastMinuteReset))
157
+  };
158
+}
159
+
160
+// 存储生成文章函数的引用
161
+let generateArticleFuncRef = null;
162
+
163
+// 更新配置
164
+function updateConfig(config) {
165
+  if (config.maxRequestsPerMinute) {
166
+    QUEUE_CONFIG.MAX_REQUESTS_PER_MINUTE = config.maxRequestsPerMinute;
167
+  }
168
+  if (config.queueCheckInterval) {
169
+    QUEUE_CONFIG.QUEUE_CHECK_INTERVAL = config.queueCheckInterval;
170
+    
171
+    // 重启队列处理器以应用新的检查间隔
172
+    if (queueProcessorInterval && generateArticleFuncRef) {
173
+      stopQueueProcessor();
174
+      startQueueProcessor(generateArticleFuncRef);
175
+    }
176
+  }
177
+}
178
+
179
+// 查找用户在队列中的位置
180
+function findUserPosition(userID) {
181
+  if (!userID) return -1;
182
+  
183
+  for (let i = 0; i < requestQueue.length; i++) {
184
+    if (requestQueue[i].userID === userID) {
185
+      return i;
186
+    }
187
+  }
188
+  
189
+  return -1;
190
+}
191
+
192
+// 检查用户是否可以发送请求(一分钟内只允许一次请求)
193
+function canUserRequest(userID) {
194
+  if (!userID) return false;
195
+  
196
+  const now = Date.now();
197
+  const lastRequestTime = userRequestMap.get(userID);
198
+  
199
+  // 如果用户没有请求记录,或者上次请求时间超过一分钟,则允许请求
200
+  if (!lastRequestTime || (now - lastRequestTime >= 60000)) {
201
+    // 更新用户最近一次请求时间
202
+    userRequestMap.set(userID, now);
203
+    return true;
204
+  }
205
+  
206
+  // 计算用户需要等待的时间(毫秒)
207
+  const waitTime = 60000 - (now - lastRequestTime);
208
+  console.log(`用户 ${userID} 请求过于频繁,需等待 ${Math.ceil(waitTime / 1000)} 秒`);
209
+  
210
+  return false;
211
+}
212
+
213
+// 清理过期的用户请求记录(可选,防止内存泄漏)
214
+function cleanupUserRequestMap() {
215
+  const now = Date.now();
216
+  const expireTime = 60000; // 一分钟过期
217
+  
218
+  userRequestMap.forEach((timestamp, userID) => {
219
+    if (now - timestamp >= expireTime) {
220
+      userRequestMap.delete(userID);
221
+    }
222
+  });
223
+}
224
+
225
+// 定期清理过期的用户请求记录
226
+setInterval(cleanupUserRequestMap, 60000);
227
+
228
+export default {
229
+  addToQueue,
230
+  canProcessImmediately,
231
+  startQueueProcessor,
232
+  stopQueueProcessor,
233
+  getQueueStatus,
234
+  updateConfig,
235
+  findUserPosition,
236
+  canUserRequest,
237
+  QUEUE_CONFIG
238
+};

+ 6 - 0
src/api/yjbdc/routes.js

@@ -1,5 +1,6 @@
1 1
 import Router from 'koa-router';
2 2
 import * as yjbdcController from './yjbdcController.js';
3
+import * as queuedArticleController from './queuedArticleController.js';
3 4
 
4 5
 const router = new Router();
5 6
 
@@ -19,5 +20,10 @@ router.get('/api/GetWordChinese',yjbdcController.GetWordChinese);
19 20
 
20 21
 router.get('/yjbdc_article_admin',yjbdcController.YJBDC_Articles_Admin);
21 22
 
23
+// 队列化文章生成API
24
+router.post('/api/QueuedGenerateArticle', queuedArticleController.QueuedGenerateArticle);
25
+router.get('/api/GetQueueStatus', queuedArticleController.GetQueueStatus);
26
+router.get('/api/GetUserRequestStatus', queuedArticleController.GetUserRequestStatus);
27
+router.post('/api/UpdateQueueConfig', queuedArticleController.UpdateQueueConfig);
22 28
 
23 29
 export default router;

+ 164 - 157
src/api/yjbdc/yjbdcController.js

@@ -15,172 +15,23 @@ import aiController from './aiController.js';
15 15
 
16 16
 import PDFDocument from 'pdfkit';
17 17
 import tencentcloud from 'tencentcloud-sdk-nodejs-ocr';
18
-
19
-const ONE_DAY_MAX_BUILD_COUNT=12;//一天最大生成数
20
-
21 18
 const OcrClient = tencentcloud.ocr.v20181119.Client;
19
+const ONE_DAY_MAX_BUILD_COUNT=12;//一天最大生成数
22 20
 
23
-//小程序登录
24
-export async function YJBDCLogin(ctx) {
25
-    let param = ctx.request.body;
26
-    if (param.param) {
27
-        const paramStr = Decrypt(param.param, config.urlSecrets.aes_key, config.urlSecrets.aes_iv);
28
-        //console.log("paramStr:"+paramStr);
29
-        param = JSON.parse(paramStr);
30
-    }
31
-    const code = param.Code;
32
-    //console.log("code:"+code);
33
-    const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${config.wx.yjbdc_appid}&secret=${config.wx.yjbdc_appsecret}&js_code=${code}&grant_type=authorization_code`;
34
-
35
-    let result = await axios.get(url)
36
-        .then(res => {
37
-            const json = res.data;
38
-            //console.log("json:"+json);
39
-            if (json && json.openid) {
40
-                param.OpenID = json.openid;
41
-                param.sessionKey = json.session_key;
42
-                if (json.unionid)
43
-                    param.UnionID = json.unionid;
44
-                return {errcode: 10000};
45
-            }
46
-            else {
47
-                return json;
48
-            }
49
-        })
50
-        .catch(err => {
51
-            return {errcode: 101, errStr: err};
52
-        });
53
-
54
-    if (result.errcode == 10000) {
55
-        delete param.Code;
56
-        if (param.sessionKey && param.iv && param.encryptedData){
57
-            //console.log("param.sessionKey:"+param.sessionKey);
58
-            const pc = new WXBizDataCrypt(config.wx.yjbdc_appid, param.sessionKey);
59
-            const dataUnionID = pc.decryptData(param.encryptedData , param.iv);
60
-            //console.log(dataUnionID);
61
-            param.UnionID = dataUnionID.unionId;
62
-        }
63
-
64
-        delete param.sessionKey;
65
-        delete param.iv;
66
-        delete param.encryptedData;
67
-
68
-        //todo
69
-        //param.OpenID="o4UHq4gaNlHfdTWxgl3fTgC1mFsI";
70
-
71
-        let userList = await yjbdc.GetUsersInfo(param);
72
-        if (userList.length > 0) {
73
-            param.LastLoginTime = new Date();
74
-            const time1 = moment(userList[0].ProductServiceTime).format('YYYY-MM-DD HH:mm:ss');
75
-            const time3 = moment().format('YYYY-MM-DD HH:mm:ss');
76
-            if (time1 < time3)
77
-                param.IsMember = 0;
78
-
79
-            delete param.Introducer;
80
-            delete param.UserSource;
81
-            delete param.SourceID;
82
-            //console.log(param.NickName);
83
-            if (param.NickName == "陌生用户") {
84
-                delete param.NickName;
85
-                delete param.AvatarUrl;
86
-                delete param.Language;
87
-                delete param.Gender;
88
-                delete param.City;
89
-                delete param.Province;
90
-                delete param.Country;
91
-            }
92
-
93
-            await yjbdc.UpdateUsers(param);
94
-            userList = await yjbdc.GetUsersInfo(param);
95
-        }
96
-        else {
97
-            param.NickName = "陌生用户";
98
-            param.AvatarUrl = "../images/userface_default.png";
99
-            param.CreateTime = new Date();
100
-            param.LastLoginTime = param.CreateTime;
101
-            param.ProductServiceTime = param.CreateTime;
102
-            const inseredID = await yjbdc.AddUsers(param);
103
-            userList = await yjbdc.GetUsersInfo(param);
104
-        }
105
-
106
-        delete userList[0].OpenID;
107
-        delete userList[0].UnionID;
108
-
109
-        //产品支付是否显示
110
-        if (param.ProgramVersion) {
111
-            let param2 = {
112
-                ProgramID: 186,
113
-                Version: param.ProgramVersion,
114
-            };
115
-            let result3 = await commonModel.GetProductVersionList(param2);
116
-            if (result3) {
117
-                if ((param2.Version == result3[0].Version && result3[0].IsShowPay <= 0)
118
-                    || param2.Version > result3[0].Version) {
119
-                    userList[0].IsShow = result3[0].IsShowPay;
120
-                }
121
-                else {
122
-                    userList[0].IsShow = 1;
123
-                }
124
-
125
-                //针对iphone测试用户,永远是无支付状态
126
-                if (userList[0].Brand == 'iPhone' && userList[0].WXLanguage == 'en-US'
127
-                    && userList[0].UserSource == '1001' && userList[0].IsPay == 0) {
128
-                    userList[0].IsShow = 0;
129
-                }
130
-
131
-                //针对微信测试用户,永远是无支付状态
132
-                if ((userList[0].UserSource=='1001' && userList[0].System=="iOS 10.0.1")
133
-                    || (!userList[0].UserSource && (!userList[0].LastUserSource || userList[0].LastUserSource>10000))
134
-                    || userList[0].NickName.indexOf("dgztest")>=0){
135
-                    userList[0].IsShow=-1;
136
-                }
137
-
138
-                if (userList[0].IsMember===1)
139
-                    userList[0].IsShow=1;
140
-            }
141
-        }
142
-
143
-        result = {errcode: 10000, result: userList[0]};
144
-    }
145
-
146
-    ctx.body = result;
147
-}
148
-
149
-//OCR获取单词
150
-export async function OCRImageData(ctx) {
151
-    const params = ctx.request.body;
152
-    
153
-    const clientConfig = {
154
-        credential: {
155
-            secretId: config.tencentcloud.secretId,
156
-            secretKey: config.tencentcloud.secretKey,
157
-        },
158
-        region: "ap-guangzhou",
159
-        profile: {
160
-            httpProfile: {
161
-                endpoint: "ocr.tencentcloudapi.com",
162
-            },
163
-        },
164
-    };
165
-
166
-    // 实例化要请求产品的client对象,clientProfile是可选的
167
-    const client = new OcrClient(clientConfig);
168
-    const result = await client.GeneralBasicOCR(params);
169 21
 
170
-    let param2={};
171
-    param2.UserID=ctx.query.UserID;
172
-    param2.CreateTime=moment().format('YYYY-MM-DD HH:mm:ss');
173
-    param2.Params=JSON.stringify(params);
174
-    param2.JSONString=JSON.stringify(result);
175
-    await yjbdc.AddOCRInfo(param2);
176
-    ctx.body = {"errcode": 10000, result};
177
-}
178 22
 
179 23
 //AI生成文章
180 24
 export async function GenerateArticle(ctx) {
181 25
 
182 26
     const url='GenerateArticle?UserID='+ctx.query.UserID;
183 27
     let result = globalCache.get(url);
28
+
29
+    // 检查是否是队列状态而不是正在处理状态
30
+    if (result && result.isQueued) {
31
+        // 如果是队列状态,继续处理
32
+        result = 0;
33
+    } 
34
+
184 35
     if (result === 0) {
185 36
         const params = ctx.request.body;
186 37
         const words = params.Words;
@@ -361,6 +212,162 @@ export async function GenerateArticle(ctx) {
361 212
     ctx.body = result;
362 213
 }
363 214
 
215
+//小程序登录
216
+export async function YJBDCLogin(ctx) {
217
+    let param = ctx.request.body;
218
+    if (param.param) {
219
+        const paramStr = Decrypt(param.param, config.urlSecrets.aes_key, config.urlSecrets.aes_iv);
220
+        //console.log("paramStr:"+paramStr);
221
+        param = JSON.parse(paramStr);
222
+    }
223
+    const code = param.Code;
224
+    //console.log("code:"+code);
225
+    const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${config.wx.yjbdc_appid}&secret=${config.wx.yjbdc_appsecret}&js_code=${code}&grant_type=authorization_code`;
226
+
227
+    let result = await axios.get(url)
228
+        .then(res => {
229
+            const json = res.data;
230
+            //console.log("json:"+json);
231
+            if (json && json.openid) {
232
+                param.OpenID = json.openid;
233
+                param.sessionKey = json.session_key;
234
+                if (json.unionid)
235
+                    param.UnionID = json.unionid;
236
+                return {errcode: 10000};
237
+            }
238
+            else {
239
+                return json;
240
+            }
241
+        })
242
+        .catch(err => {
243
+            return {errcode: 101, errStr: err};
244
+        });
245
+
246
+    if (result.errcode == 10000) {
247
+        delete param.Code;
248
+        if (param.sessionKey && param.iv && param.encryptedData){
249
+            //console.log("param.sessionKey:"+param.sessionKey);
250
+            const pc = new WXBizDataCrypt(config.wx.yjbdc_appid, param.sessionKey);
251
+            const dataUnionID = pc.decryptData(param.encryptedData , param.iv);
252
+            //console.log(dataUnionID);
253
+            param.UnionID = dataUnionID.unionId;
254
+        }
255
+
256
+        delete param.sessionKey;
257
+        delete param.iv;
258
+        delete param.encryptedData;
259
+
260
+        //todo
261
+        //param.OpenID="o4UHq4gaNlHfdTWxgl3fTgC1mFsI";
262
+
263
+        let userList = await yjbdc.GetUsersInfo(param);
264
+        if (userList.length > 0) {
265
+            param.LastLoginTime = new Date();
266
+            const time1 = moment(userList[0].ProductServiceTime).format('YYYY-MM-DD HH:mm:ss');
267
+            const time3 = moment().format('YYYY-MM-DD HH:mm:ss');
268
+            if (time1 < time3)
269
+                param.IsMember = 0;
270
+
271
+            delete param.Introducer;
272
+            delete param.UserSource;
273
+            delete param.SourceID;
274
+            //console.log(param.NickName);
275
+            if (param.NickName == "陌生用户") {
276
+                delete param.NickName;
277
+                delete param.AvatarUrl;
278
+                delete param.Language;
279
+                delete param.Gender;
280
+                delete param.City;
281
+                delete param.Province;
282
+                delete param.Country;
283
+            }
284
+
285
+            await yjbdc.UpdateUsers(param);
286
+            userList = await yjbdc.GetUsersInfo(param);
287
+        }
288
+        else {
289
+            param.NickName = "陌生用户";
290
+            param.AvatarUrl = "../images/userface_default.png";
291
+            param.CreateTime = new Date();
292
+            param.LastLoginTime = param.CreateTime;
293
+            param.ProductServiceTime = param.CreateTime;
294
+            const inseredID = await yjbdc.AddUsers(param);
295
+            userList = await yjbdc.GetUsersInfo(param);
296
+        }
297
+
298
+        delete userList[0].OpenID;
299
+        delete userList[0].UnionID;
300
+
301
+        //产品支付是否显示
302
+        if (param.ProgramVersion) {
303
+            let param2 = {
304
+                ProgramID: 186,
305
+                Version: param.ProgramVersion,
306
+            };
307
+            let result3 = await commonModel.GetProductVersionList(param2);
308
+            if (result3) {
309
+                if ((param2.Version == result3[0].Version && result3[0].IsShowPay <= 0)
310
+                    || param2.Version > result3[0].Version) {
311
+                    userList[0].IsShow = result3[0].IsShowPay;
312
+                }
313
+                else {
314
+                    userList[0].IsShow = 1;
315
+                }
316
+
317
+                //针对iphone测试用户,永远是无支付状态
318
+                if (userList[0].Brand == 'iPhone' && userList[0].WXLanguage == 'en-US'
319
+                    && userList[0].UserSource == '1001' && userList[0].IsPay == 0) {
320
+                    userList[0].IsShow = 0;
321
+                }
322
+
323
+                //针对微信测试用户,永远是无支付状态
324
+                if ((userList[0].UserSource=='1001' && userList[0].System=="iOS 10.0.1")
325
+                    || (!userList[0].UserSource && (!userList[0].LastUserSource || userList[0].LastUserSource>10000))
326
+                    || userList[0].NickName.indexOf("dgztest")>=0){
327
+                    userList[0].IsShow=-1;
328
+                }
329
+
330
+                if (userList[0].IsMember===1)
331
+                    userList[0].IsShow=1;
332
+            }
333
+        }
334
+
335
+        result = {errcode: 10000, result: userList[0]};
336
+    }
337
+
338
+    ctx.body = result;
339
+}
340
+
341
+//OCR获取单词
342
+export async function OCRImageData(ctx) {
343
+    const params = ctx.request.body;
344
+    
345
+    const clientConfig = {
346
+        credential: {
347
+            secretId: config.tencentcloud.secretId,
348
+            secretKey: config.tencentcloud.secretKey,
349
+        },
350
+        region: "ap-guangzhou",
351
+        profile: {
352
+            httpProfile: {
353
+                endpoint: "ocr.tencentcloudapi.com",
354
+            },
355
+        },
356
+    };
357
+
358
+    // 实例化要请求产品的client对象,clientProfile是可选的
359
+    const client = new OcrClient(clientConfig);
360
+    const result = await client.GeneralBasicOCR(params);
361
+
362
+    let param2={};
363
+    param2.UserID=ctx.query.UserID;
364
+    param2.CreateTime=moment().format('YYYY-MM-DD HH:mm:ss');
365
+    param2.Params=JSON.stringify(params);
366
+    param2.JSONString=JSON.stringify(result);
367
+    await yjbdc.AddOCRInfo(param2);
368
+    ctx.body = {"errcode": 10000, result};
369
+}
370
+
364 371
 export async function GetYJBDCGenerateConfig(ctx) {
365 372
     const param = {
366 373
         UserID: ctx.query.UserID || 0,

+ 1 - 1
src/test/build.test.js

@@ -52,7 +52,7 @@ async function runScript(){
52 52
 
53 53
             //生成例句
54 54
             let result = await aiController.generateArticle(content, aiProvider);
55
-            //console.log(result); 
55
+            console.log(result); 
56 56
             let sql2="update Words set ExampleSentence=? where ID="+item.ID+";";
57 57
             await commonModel.RunSql(result,sql2);
58 58
             

+ 0 - 15
test-aliyun-api.js

@@ -1,15 +0,0 @@
1
-// 测试阿里云通义千问API调用
2
-import { generateArticle } from './src/api/yjbdc/aiController.js';
3
-
4
-async function testAliyunAPI() {
5
-  try {
6
-    console.log('开始测试阿里云通义千问API...');
7
-    const result = await generateArticle('请简要介绍一下人工智能的发展历程', 'aliyun');
8
-    console.log('API调用成功,返回结果:');
9
-    console.log(result);
10
-  } catch (error) {
11
-    console.error('API调用失败:', error);
12
-  }
13
-}
14
-
15
-testAliyunAPI();

+ 0 - 61
test-json-fix.js

@@ -1,61 +0,0 @@
1
-// 测试JSON修复功能
2
-import { validateAndFixJSON } from './src/api/yjbdc/aiController.js';
3
-
4
-// 测试用例
5
-const testCases = [
6
-  // 正常JSON
7
-  { 
8
-    name: "正常JSON", 
9
-    input: '{"name":"test","value":123}' 
10
-  },
11
-  // Markdown代码块包裹的JSON
12
-  { 
13
-    name: "Markdown代码块", 
14
-    input: '```json\n{"name":"test","value":123}\n```' 
15
-  },
16
-  // 带有语法错误的JSON
17
-  { 
18
-    name: "语法错误JSON", 
19
-    input: '{name:"test",value:123}' 
20
-  },
21
-  // 混合问题
22
-  { 
23
-    name: "混合问题", 
24
-    input: '```json\n{name:"test",value:123}\n```' 
25
-  },
26
-  // 非JSON内容中包含JSON对象
27
-  {
28
-    name: "非JSON内容中包含JSON对象",
29
-    input: '这是一段文本,其中包含JSON:{"name":"test","value":123},后面还有其他内容'
30
-  },
31
-  // 阿里云通义千问可能返回的格式
32
-  {
33
-    name: "阿里云通义千问返回格式",
34
-    input: '```json\n{\n  "title": "测试文章",\n  "content": "这是一篇测试文章",\n  "author": "AI助手",\n  "date": "2023-07-15"\n}\n```'
35
-  }
36
-];
37
-
38
-// 运行测试
39
-console.log("开始测试JSON修复功能...\n");
40
-
41
-testCases.forEach(testCase => {
42
-  console.log(`测试用例: ${testCase.name}`);
43
-  console.log(`输入: ${testCase.input}`);
44
-  
45
-  try {
46
-    const fixed = validateAndFixJSON(testCase.input);
47
-    console.log(`修复后: ${fixed}`);
48
-    
49
-    try {
50
-      const parsed = JSON.parse(fixed);
51
-      console.log(`解析结果: ${JSON.stringify(parsed, null, 2)}`);
52
-      console.log("✅ 测试通过\n");
53
-    } catch (error) {
54
-      console.error(`❌ 修复后仍无法解析: ${error.message}\n`);
55
-    }
56
-  } catch (error) {
57
-    console.error(`❌ 修复过程出错: ${error.message}\n`);
58
-  }
59
-});
60
-
61
-console.log("测试完成");