chengjie před 9 měsíci
rodič
revize
edc62b5c84

binární
public/images/acode/ScoreLineArticle_-173.png


binární
public/images/acode/YJBDC_QRCode.png


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

@@ -8,6 +8,7 @@ router.post('/api/OCRImageData',yjbdcController.OCRImageData);
8
 router.post('/api/GenerateArticle',yjbdcController.GenerateArticle);
8
 router.post('/api/GenerateArticle',yjbdcController.GenerateArticle);
9
 router.post('/api/GeneratePDF',yjbdcController.GeneratePDF);
9
 router.post('/api/GeneratePDF',yjbdcController.GeneratePDF);
10
 router.get('/api/GetYJBDCArticleList',yjbdcController.GetYJBDCArticleList);
10
 router.get('/api/GetYJBDCArticleList',yjbdcController.GetYJBDCArticleList);
11
+router.get('/api/BuildYJBDCQRCode',yjbdcController.BuildYJBDCQRCode);
11
 router.get('/api/DeleteYJBDCArticleList',yjbdcController.DeleteYJBDCArticleList);
12
 router.get('/api/DeleteYJBDCArticleList',yjbdcController.DeleteYJBDCArticleList);
12
 
13
 
13
 export default router;
14
 export default router;

+ 100 - 7
src/api/yjbdc/yjbdcController.js

@@ -1,4 +1,5 @@
1
 import moment from 'moment';
1
 import moment from 'moment';
2
+import fs from 'fs';
2
 import commonModel from '../../model/commonModel.js';
3
 import commonModel from '../../model/commonModel.js';
3
 import config from '../../config/index.js';
4
 import config from '../../config/index.js';
4
 import _ from 'lodash';
5
 import _ from 'lodash';
@@ -181,7 +182,7 @@ export async function GenerateArticle(ctx) {
181
     let result = globalCache.get(url);
182
     let result = globalCache.get(url);
182
     if (result === 0) {
183
     if (result === 0) {
183
         const params = ctx.request.body;
184
         const params = ctx.request.body;
184
-        const words = JSON.parse(params.Words).join(",");
185
+        const words = params.Words;
185
         const articleStyle = params.ArticleStyle;
186
         const articleStyle = params.ArticleStyle;
186
 
187
 
187
         //文章类型
188
         //文章类型
@@ -199,6 +200,7 @@ export async function GenerateArticle(ctx) {
199
             "动物":"生活在地球上的各种生物,有的会跑,有的会飞,有的会游泳,比如猫、狗、大象、企鹅,它们都有自己的特点和习性。",
200
             "动物":"生活在地球上的各种生物,有的会跑,有的会飞,有的会游泳,比如猫、狗、大象、企鹅,它们都有自己的特点和习性。",
200
             "环保":"指环境保护、保护大自然,让地球更干净、更健康。比如节约用水、减少垃圾、种树、不乱扔塑料袋,这样空气会更清新,动物也有更好的家园。每个人都可以从小事做起爱护地球。",
201
             "环保":"指环境保护、保护大自然,让地球更干净、更健康。比如节约用水、减少垃圾、种树、不乱扔塑料袋,这样空气会更清新,动物也有更好的家园。每个人都可以从小事做起爱护地球。",
201
         };
202
         };
203
+
202
         //等级难度
204
         //等级难度
203
         const LEVEL=[
205
         const LEVEL=[
204
             {
206
             {
@@ -221,7 +223,7 @@ export async function GenerateArticle(ctx) {
221
 
223
 
222
         let content = "将"+words+"这些单词生成一篇英文文章。要求"+
224
         let content = "将"+words+"这些单词生成一篇英文文章。要求"+
223
         "[难度];"+
225
         "[难度];"+
224
-        "单词若是脏话,像'f***'、's***'等,可以忽略;"+
226
+        "单词若是脏话,像'fuck'、'shit'等,可以忽略;"+
225
         "文章类型是'"+articleStyle+"([类型])';"+
227
         "文章类型是'"+articleStyle+"([类型])';"+
226
         "文章单词数在200个左右,最多不能超过300个;"+
228
         "文章单词数在200个左右,最多不能超过300个;"+
227
         "文章按每句分成数组,且每句都有中文翻译;"+
229
         "文章按每句分成数组,且每句都有中文翻译;"+
@@ -232,6 +234,8 @@ export async function GenerateArticle(ctx) {
232
 
234
 
233
         content = content.replace("[难度]",LEVEL[Number(params.Level)].Content);
235
         content = content.replace("[难度]",LEVEL[Number(params.Level)].Content);
234
         
236
         
237
+        if (articleStyle=="任意")
238
+            articleStyle=stringUtils.Random(0,ARTICLE_STYLE.length-1);
235
         content = content.replace("[类型]",ARTICLE_STYLE[articleStyle]);
239
         content = content.replace("[类型]",ARTICLE_STYLE[articleStyle]);
236
         
240
         
237
 
241
 
@@ -390,7 +394,7 @@ export async function GeneratePDF(ctx) {
390
             try {
394
             try {
391
                 if (typeof content.Words === 'string') {
395
                 if (typeof content.Words === 'string') {
392
                     // 尝试解析JSON字符串
396
                     // 尝试解析JSON字符串
393
-                    words = JSON.parse(content.Words);
397
+                    words = content.Words.join(",");
394
                 } else if (Array.isArray(content.Words)) {
398
                 } else if (Array.isArray(content.Words)) {
395
                     // 已经是数组
399
                     // 已经是数组
396
                     words = content.Words;
400
                     words = content.Words;
@@ -424,7 +428,7 @@ export async function GeneratePDF(ctx) {
424
            .text(articleText, pixelToPt(120), pixelToPt(250), {
428
            .text(articleText, pixelToPt(120), pixelToPt(250), {
425
                width: pixelToPt(1240),
429
                width: pixelToPt(1240),
426
                align: 'left',
430
                align: 'left',
427
-               lineGap: pixelToPt(10)  // 行间距
431
+               lineGap: pixelToPt(50)  // 行间距
428
            });
432
            });
429
 
433
 
430
         // 记录文章结束位置的y坐标
434
         // 记录文章结束位置的y坐标
@@ -503,8 +507,9 @@ export async function GeneratePDF(ctx) {
503
                        align: 'left'
507
                        align: 'left'
504
                    });
508
                    });
505
                 
509
                 
506
-                // 问题选项
507
-                doc.fontSize(pixelToPt(36));
510
+                // 问题选项 - 使用常规字体(Regular)
511
+                doc.font('Helvetica') // 明确设置为常规字体,不使用粗体
512
+                   .fontSize(pixelToPt(36));
508
                 
513
                 
509
                 const options = questions[0].OptionsEnglish || [];
514
                 const options = questions[0].OptionsEnglish || [];
510
                 let optionY = doc.y + pixelToPt(20);
515
                 let optionY = doc.y + pixelToPt(20);
@@ -694,7 +699,25 @@ export async function GeneratePDF(ctx) {
694
                width: pixelToPt(380),
699
                width: pixelToPt(380),
695
                align: 'right'
700
                align: 'right'
696
            });
701
            });
697
-
702
+        
703
+        // 添加二维码图片
704
+        try {
705
+            // 计算图片位置:距离右边120像素,距离底部100像素
706
+            // 由于PDFKit使用左上角坐标系,需要计算左上角坐标
707
+            const qrCodeWidth = pixelToPt(200);
708
+            const qrCodeHeight = pixelToPt(200);
709
+            const qrCodeX = doc.page.width - pixelToPt(120) - qrCodeWidth; // 右边距离转换为左边距离
710
+            const qrCodeY = doc.page.height - pixelToPt(60) - qrCodeHeight; // 底部距离转换为顶部距离
711
+            
712
+            // 添加二维码图片
713
+            doc.image('./public/images/acode/YJBDC_QRCode.png', qrCodeX, qrCodeY, {
714
+                width: qrCodeWidth,
715
+                height: qrCodeHeight
716
+            });
717
+            console.log("QR Code added successfully");
718
+        } catch (imgError) {
719
+            console.error("Error adding QR Code image:", imgError);
720
+        }
698
         
721
         
699
         //console.log("PDF generation completed, finalizing document...");
722
         //console.log("PDF generation completed, finalizing document...");
700
 
723
 
@@ -735,4 +758,74 @@ export async function GeneratePDF(ctx) {
735
         ctx.status = 500;
758
         ctx.status = 500;
736
         ctx.body = { error: error.message, stack: error.stack };
759
         ctx.body = { error: error.message, stack: error.stack };
737
     }
760
     }
761
+}
762
+
763
+export async function BuildYJBDCQRCode(ctx) {
764
+    try {
765
+        // 获取微信访问令牌
766
+        const tokenUrl = `https://api.weixin.qq.com/cgi-bin/token?appid=${config.wx.yjbdc_appid}&secret=${config.wx.yjbdc_appsecret}&grant_type=client_credential`;
767
+        
768
+        const tokenResponse = await axios.get(tokenUrl);
769
+        const tokenData = tokenResponse.data;
770
+        
771
+        if (!tokenData || !tokenData.access_token) {
772
+            ctx.status = 400;
773
+            ctx.body = { errcode: 101, errStr: '获取微信访问令牌失败' };
774
+            return;
775
+        }
776
+        
777
+        const accessToken = tokenData.access_token;
778
+        
779
+        // 生成小程序码
780
+        const qrCodeUrl = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${accessToken}`;
781
+        const path = './public/images/acode/';
782
+        const imageUrl = 'YJBDC_QRCode.png';
783
+        const fullPath = path + imageUrl;
784
+        
785
+        // 确保目录存在
786
+        if (!fs.existsSync(path)) {
787
+            fs.mkdirSync(path, { recursive: true });
788
+        }
789
+        
790
+        const postData = {
791
+            width: 280,
792
+            scene: "SourceID=187"
793
+        };
794
+        
795
+        // 使用axios获取二维码并保存到文件
796
+        const qrCodeResponse = await axios({
797
+            method: 'POST',
798
+            url: qrCodeUrl,
799
+            data: postData,
800
+            responseType: 'stream'
801
+        });
802
+        
803
+        // 创建写入流
804
+        const writer = fs.createWriteStream(fullPath);
805
+        
806
+        // 将响应数据写入文件
807
+        qrCodeResponse.data.pipe(writer);
808
+        
809
+        // 返回成功响应
810
+        ctx.body = { errcode: 10000, message: "二维码生成请求已发送" };
811
+        
812
+        // 处理文件写入完成事件
813
+        writer.on('finish', () => {
814
+            console.log("二维码生成成功:", fullPath);
815
+        });
816
+        
817
+        // 处理错误
818
+        writer.on('error', (err) => {
819
+            console.error("二维码文件写入失败:", err);
820
+        });
821
+        
822
+    } catch (error) {
823
+        console.error("生成二维码失败:", error);
824
+        ctx.status = 500;
825
+        ctx.body = { 
826
+            errcode: 500, 
827
+            errStr: '生成二维码失败', 
828
+            error: error.message 
829
+        };
830
+    }
738
 }
831
 }