certificate.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. // =============================================================================
  2. var express = require("express");
  3. var request = require("request");
  4. var bcrypt = require("bcryptjs");
  5. let axios = require("axios");
  6. var router = express.Router(); // get an instance of the express Router
  7. const querystring = require("querystring");
  8. const dateformat = require('./comment/dateformat');
  9. var mysql = require("./mysql");
  10. const PDFDocument = require('pdfkit');
  11. // const PDFDocument = require('@hollandjake/pdfkit-table');
  12. const fs = require('fs');
  13. const _mysqlLabor = ["183.36.26.8", "sc_app"]; // 提交的使用用这两个edu數據庫信息
  14. /**
  15. * 获取某年的证书设置
  16. */
  17. router.get('/getseeting', async (req, res, next) => {
  18. let year = req.query.year || new Date().getFullYear();
  19. let data = await mysql.sqlHandler(_mysqlLabor, 'select * from certificate_seeting where year=' + year);
  20. if (data && data.length > 0)
  21. res.json({
  22. status: true,
  23. data: data[0]
  24. });
  25. else
  26. res.json({
  27. status: false,
  28. err: "没有数据"
  29. })
  30. })
  31. async function saveSetting(data) {
  32. try {
  33. data.daterange_start = dateformat.dateFormat(data.daterange_start);
  34. data.daterange_end = dateformat.dateFormat(data.daterange_end);
  35. let res = await mysql.sqlHandler(_mysqlLabor, 'select * from certificate_seeting where year=' + data.year);
  36. if (res && res.length > 0) {
  37. return await mysql.sqlHandler(_mysqlLabor, `update certificate_seeting set daterange_start='${data.daterange_start}',daterange_end='${data.daterange_end}',assclasshour=${data.assclasshour},minclasshour=${data.minclasshour} where year=${data.year}`);
  38. } else {
  39. return await mysql.sqlHandler(_mysqlLabor, `insert into certificate_seeting(year,daterange_start,daterange_end,assclasshour,minclasshour) values(${data.year},'${data.daterange_start}','${data.daterange_end}',${data.assclasshour},${data.minclasshour})`);
  40. }
  41. } catch (err) {
  42. return false;
  43. }
  44. }
  45. /**
  46. * 保存证书设置
  47. */
  48. router.post('/savesetting', async (req, res, next) => {
  49. let fromCollection = req.body;
  50. let save = await saveSetting(fromCollection);
  51. if (save)
  52. res.json({ status: true })
  53. else
  54. res.json({ status: false, err: '保存失败' })
  55. });
  56. async function saveBack(data) {
  57. try {
  58. let res = await mysql.sqlHandler(_mysqlLabor, 'select * from certificate_seeting where year=' + data.year);
  59. if (res && res.length > 0) {
  60. let sqlString = `update certificate_seeting set backimg='${data.mapImageUrl}',sealimg='${data.stampUrl}', namefontsize=${data.font[0].fontSize},namefontx=${data.font[0].x},namefonty=${data.font[0].y},namefongcolor='${data.font[0].color}',classhoursize=${data.font[1].fontSize},classhourx=${data.font[1].x},classhoury=${data.font[1].y},classhourcolor='${data.font[1].color}',nosize=${data.font[2].fontSize},nox=${data.font[2].x},noy=${data.font[2].y},nocolor='${data.font[2].color}',datesize=${data.font[3].fontSize},datex=${data.font[3].x},datey=${data.font[3].y},datecolor='${data.font[3].color}',stampsize=${data.stamp.size},stamx=${data.stamp.x},stamy=${data.stamp.y} where year=${data.year}`;
  61. let save = await mysql.sqlHandler(_mysqlLabor, sqlString);
  62. if (save)
  63. return {
  64. status: true
  65. }
  66. else
  67. return {
  68. status: false,
  69. err: '保存失败'
  70. }
  71. } else
  72. return {
  73. status: false,
  74. err: '请先保存证书配置'
  75. }
  76. } catch (err) {
  77. console.log('err', err);
  78. return false;
  79. }
  80. }
  81. /**
  82. * 保存证书信息
  83. */
  84. router.post('/saveback', async (req, res, next) => {
  85. let fromCollection = req.body;
  86. let save = await saveBack(fromCollection);
  87. res.json(save);
  88. })
  89. /**
  90. * 获取当前用户某个年度的课时以及报名的活动列表
  91. * @param {*} acid
  92. */
  93. async function getUserCertCount(year, openid, pageIndex, pageSize) {
  94. //获取改年的起始时间
  95. let settingObj = await mysql.sqlHandler(_mysqlLabor, `select * from certificate_seeting where year=${year}`);
  96. if (settingObj && settingObj.length > 0) {
  97. let sqlString = `select a.*,b.acName,b.pic,b.acshape,b.endTime,b.address from activity_apply_const as a left join activity as b on a.acid=b.acId where a.year=${year} and a.openid='${openid}' order by a.create_at desc limit ${(pageIndex - 1) * pageSize},${pageSize}`;
  98. let applayList = await mysql.sqlHandler(_mysqlLabor, sqlString);
  99. let statics = await mysql.sqlHandler(_mysqlLabor, `select sum(classhour) as classhour from activity_apply_const where openid='${openid}' and year=${year}`);
  100. return {
  101. status: true,
  102. data: {
  103. applayList: applayList,
  104. settingObj: settingObj && settingObj.length > 0 ? settingObj[0] : {},
  105. statics: statics && statics.length > 0 ? statics[0] : {}
  106. }
  107. }
  108. } else
  109. return {
  110. status: false,
  111. err: '该年度没有初始化证书设置'
  112. }
  113. }
  114. /**
  115. * 写入活动课时
  116. * @param {*} acid
  117. * @param {*} openid
  118. * @param {*} classhour
  119. * @returns
  120. */
  121. async function creatUserApplyActClassHour(acid, openid) {
  122. //获取当前年
  123. let year = new Date().getFullYear();
  124. //获取活动内容
  125. let actInfo = await mysql.sqlHandler(_mysqlLabor, `select a.* from activity as a where a.acId='${acid}'`);
  126. if (actInfo && actInfo.length > 0) {
  127. actInfo = actInfo[0];
  128. let acYear = new Date(actInfo.create_at).getFullYear();
  129. //查询是否已经记录该课时
  130. let hasClassHour = await mysql.sqlHandler(_mysqlLabor, `select * from activity_apply_const where acid='${acid}' and openid='${openid}'`);
  131. if (hasClassHour && hasClassHour.length > 0)
  132. return { status: false, err: '已经记录' };
  133. //查询该年的证书课时配置
  134. let certInfo = await mysql.sqlHandler(_mysqlLabor, `select * from certificate_seeting where year=${acYear}`);
  135. if (certInfo && certInfo.length > 0) {
  136. certInfo = certInfo[0];
  137. let assclasshour = certInfo.assclasshour;
  138. let insertRes = await mysql.sqlHandler(_mysqlLabor, `insert into activity_apply_const(acid,openid,classhour,year,create_at) values('${acid}','${openid}',${assclasshour},${acYear},'${dateformat.dateFormat(new Date())}')`);
  139. if (insertRes)
  140. return { status: true };
  141. else
  142. return { status: false, err: '保存失败' };
  143. } else
  144. return { status: false, err: '该年度证书配置无效' };
  145. } else
  146. return { status: false, err: '没有该活动' }
  147. }
  148. /**
  149. * 获取用户证书详情
  150. */
  151. router.get('/getusercertdetail', async (req, res, next) => {
  152. let fromCollection = req.query;
  153. let data = await getUserCertCount(fromCollection.year, fromCollection.openid, fromCollection.pageIndex || 1, fromCollection.pageSize || 20);
  154. res.json(data);
  155. })
  156. async function getStaticsYearForAdmin(year, pageIndex, pageSize, username = "") {
  157. let otherWhere = username != '' ? ' and c.username like "%' + username + '%"' : '';
  158. let sqlString = `select a.*,b.acName,b.pic,b.acshape,b.endTime,b.address,c.username,c.schoolName from activity_apply_const as a left join activity as b on a.acid=b.acId left join user as c on a.openid=c.openid where a.year=${year} ${otherWhere} order by a.create_at desc limit ${(pageIndex - 1) * pageSize},${pageSize}`;
  159. let list = await mysql.sqlHandler(_mysqlLabor, sqlString);
  160. let count = await mysql.sqlHandler(_mysqlLabor, `select count(a.id) as count from activity_apply_const as a left join user as c on a.openid=c.openid where a.year=${year} ${otherWhere}`);
  161. let statics = await mysql.sqlHandler(_mysqlLabor, `select sum(a.classhour) as classhour from activity_apply_const as a left join user as c on a.openid=c.openid where year=${year} ${otherWhere}`);
  162. return {
  163. status: true,
  164. data: {
  165. list: list,
  166. count: count && count.length > 0 ? count[0].count : 0,
  167. classhour: statics && statics.length > 0 ? statics[0].classhour : 0
  168. }
  169. }
  170. }
  171. /**
  172. * 获取统计数据
  173. */
  174. router.get('/getstaticsyearforadmin', async (req, res, next) => {
  175. let year = req.query.year || new Date().getFullYear();
  176. let pageIndex = req.query.pageIndex || 1;
  177. let pageSize = req.query.pageSize || 10;
  178. let username = req.query.username || '';
  179. let data = await getStaticsYearForAdmin(year, pageIndex, pageSize, username);
  180. res.json(data);
  181. })
  182. /**
  183. * 生成PDF
  184. * @param {*} openid
  185. * @param {*} year
  186. */
  187. async function creatPdf(openid, year) {
  188. // 创建一个 PDF 文档实例
  189. const doc = new PDFDocument();
  190. process.stdout.setDefaultEncoding('utf8');
  191. // 将文档内容通过管道写入一个文件
  192. doc.pipe(fs.createWriteStream(`./static/files/pdf-${openid}-${year}.pdf`));
  193. // const response = await axios({
  194. // method: 'get',
  195. // url: 'https://teacherapi.cocorobo.cn/teaching-file/static/yym/Rectangle25.png',
  196. // responseType: 'arraybuffer' // 确保我们获取到的是Buffer类型的数据
  197. // });
  198. // const imageBuffer = Buffer.from(response.data, 'binary');
  199. // doc.registerFont('main', './msyh.TTF')
  200. // doc.moveDown().image(imageBuffer, {
  201. // width: 400,
  202. // align: 'center', // 居中对齐图片
  203. // valign: 'center',
  204. // x: 100,
  205. // y: 100
  206. // });
  207. let bigTitleSize = 30;
  208. let contentSize = 13;
  209. let listSize = 12;
  210. // 添加标题
  211. doc.font('./msyh.TTF').fontSize(bigTitleSize).fillColor('#000000').text('继续教育证明', {
  212. paragraphGap: 5,
  213. align: 'center'
  214. });
  215. doc.font('./msyh.TTF').fontSize(contentSize).fillColor('#000000').text('CONTINUING EDUCATION CERTIFICATE', {
  216. paragraphGap: 5,
  217. align: 'center'
  218. });
  219. //获取用户数据
  220. let user = await mysql.sqlHandler(_mysqlLabor, `select * from user where openid='${openid}'`);
  221. let statics = await mysql.sqlHandler(_mysqlLabor, `select sum(classhour) as classhour from activity_apply_const where year=${year} and openid='${openid}'`);
  222. if (user && user.length > 0 && statics && statics.length > 0) {
  223. user = user[0];
  224. let classhour = statics[0].classhour || 0;
  225. // 添加段落
  226. doc.font('./msyh.TTF').moveDown().fontSize(contentSize).fillColor('#000000').text(`${user.username}老师(${user.schoolName}),于${year}年度期间,积极参加“丽湖职教双创教育国际虚拟教研室”系列教研活动。该教师在该年度学习期间,累计完成继续教育课程(${classhour}学时),具体参与情况如下:`, {
  227. indent: contentSize * 2,
  228. lineGap: 5,
  229. align: 'left'
  230. });
  231. }
  232. //获取活动数据
  233. let sqlString = `select a.*,b.acName,b.pic,b.acshape,b.endTime,b.address,c.username,c.schoolName from activity_apply_const as a left join activity as b on a.acid=b.acId left join user as c on a.openid=c.openid where a.year=${year} and a.openid='${openid}' order by a.create_at desc`;
  234. let list = await mysql.sqlHandler(_mysqlLabor, sqlString);
  235. if (list && list.length > 0) {
  236. for (let i = 0; i < list.length; i++) {
  237. doc.font('./msyh.TTF').moveDown().fontSize(listSize).fillColor('#333333').text(`${i + 1}. ${list[i].acName},${dateformat.dateFormat(list[i].create_at, "year-month-day")},${list[i].classhour}课时`, {
  238. indent: 0,
  239. lineGap: 4,
  240. align: 'left'
  241. });
  242. }
  243. }
  244. // 链式调用构建表格
  245. //主板单位
  246. doc.font('./msyh.TTF').moveDown().moveDown().moveDown().fontSize(contentSize).fillColor('#000000').text('主办单位:', {
  247. paragraphGap: 4,
  248. lineGap: 3,
  249. align: 'right'
  250. });
  251. let dwList = ['深圳职业技术大学工业训练中心(创新创业学院)(代章)', '南京工业职业技术大学创新创业学院', '浙江工贸职业技术学院创业学院', '深圳职业技术大学联合国教科文组织职业教育计划亚非研究与培训中心', '学堂在线'];
  252. dwList.forEach(item => {
  253. doc.font('./msyh.TTF').fontSize(contentSize).fillColor('#000000').text(item, {
  254. paragraphGap: 3,
  255. lineGap: 3,
  256. align: 'right'
  257. });
  258. })
  259. //日期
  260. doc.font('./msyh.TTF').moveDown().fontSize(contentSize).fillColor('#000000').text(`日期:${dateformat.dateFormat(new Date(), "year-month-day")}`, {
  261. align: 'right'
  262. });
  263. //盖章
  264. const pageWidth = doc.page.width;
  265. const pageHeight = doc.page.height;
  266. const margins = doc.page.margins;
  267. const contentWidth = pageWidth - margins.left - margins.right;
  268. const contentHeight = pageHeight - margins.top - margins.bottom;
  269. doc.moveDown(-5).image('./static/yinzhang2026.png', { x: contentWidth - 30, width: 130 });
  270. // 结束文档
  271. doc.end();
  272. return `pdf-${openid}-${year}.pdf`;
  273. }
  274. router.post('/downloadcert', async (req, res, next) => {
  275. let openid = req.body.openid;
  276. let year = req.body.year;
  277. if (fs.existsSync(`./static/files/pdf-${openid}-${year}.pdf`)) {
  278. res.json({
  279. status: true,
  280. data: `/static/files/pdf-${openid}-${year}.pdf`
  281. })
  282. } else {
  283. let pdfPath = await creatPdf(openid, year);
  284. if (pdfPath && pdfPath != '') {
  285. res.json({
  286. status: true,
  287. data: `/static/files/${pdfPath}`
  288. })
  289. } else
  290. res.json({
  291. status: false,
  292. err: 'pdf生成失败'
  293. })
  294. }
  295. })
  296. // creatPdf('oe5Ow63GMRguC3D_jROKKFOWdwHc', '2025');
  297. router.creatUserApplyActClassHour = creatUserApplyActClassHour;
  298. module.exports = router;