11wqe1 4 days ago
parent
commit
009bc24bef

BIN
DejaVuSans.ttf


+ 29 - 8
app.js

@@ -1,16 +1,27 @@
-// call the packages we need
-var express = require("express"); // call express
-var app = express(); // define our app using express
+const express = require('express');
+const http = require('http');
+const router = express.Router();
+const app = express();
+const fs = require('fs');
 var bodyParser = require("body-parser");
 var request = require("request");
 const edurouter = require("./pbl");
-const morgan = require("morgan");
+const certificate = require("./certificate");
 var path = require("path");
+
 var port = "7333"; // set our port
-//const cors = require('cors')
-app.use(morgan("dev"));
-// configure app to use bodyParser()
-// this will let us get the data from a POST
+
+
+process.env.NODE_ENV = 'dev';
+// main.use((req, res, next) => {
+//     var deviceAgent = req.headers['user-agent'].toLowerCase();
+//     var agentID = deviceAgent.match(/(iphone|ipod|ipad|android)/);
+//     if (agentID)
+//         req.ismobile = true;
+//     else
+//         req.ismobile = false;
+//     next();
+// });
 app.use(bodyParser.urlencoded({ extended: true, limit: "3mb" }));
 app.use(bodyParser.json({ limit: "3mb" }));
 
@@ -49,6 +60,9 @@ app.use(verifToken);
 
 // all of our routes will be prefixed with /api
 app.use("/api/pbl", edurouter);
+app.use("/api/pbl", certificate);
+// 设置静态目录,带虚拟前缀
+app.use('/static', express.static(path.join(__dirname, 'static')));
 // app.use('/game', game);
 
 app.all("/download", function (req, res, next) {
@@ -80,3 +94,10 @@ app.all("/download", function (req, res, next) {
 // =============================================================================
 app.listen(port);
 console.log("app happens on port " + port);
+app.use(function (req, res, next) {
+
+    res.send("404 Not Found");
+});
+
+
+module.exports = app;

+ 26 - 0
bin/httpserver.js

@@ -0,0 +1,26 @@
+const http = require('http');
+const path = require('path');
+
+
+/**
+ * http服务启动
+ * @param {*} port 
+ * @param {*} app 
+ */
+function httpServerStart(port, app) {
+    const server = http.createServer(app);
+    app.set('port', port);
+    return new Promise((resolve, reject) => {
+        server.listen(port);
+        server.on('error', (err) => {
+            reject(err);
+        });
+        server.on('listening', () => {
+            console.log('http server ready now');
+            resolve(server);
+        });
+    })
+
+}
+
+module.exports.httpServerStart = httpServerStart;

+ 34 - 0
bin/httpsslserver.js

@@ -0,0 +1,34 @@
+const https = require('https');
+const debug = require('debug')('ultrong:httpsserver');
+const path = require('path');
+const config = require('../config/server');
+const fs = require('fs');
+
+/**
+ * SSlServer
+ * @param {*} port 
+ * @param {*} app 
+ */
+function httpSslServerStart(port, app) {
+    /*证书配置*/
+    const options = {
+        key: fs.readFileSync(config.https.certificate.key),
+        cert: fs.readFileSync(config.https.certificate.cert),
+        crt:fs.readFileSync(config.https.certificate.crt)
+    }
+    /*启动sslserver*/
+    return new Promise((resolve, reject) => {
+        app.set('sslPort', port);
+        const sslServer = https.createServer(options,app);
+        sslServer.listen(port);
+        sslServer.on('error', err => {
+            reject(err);
+        });
+        sslServer.on('listening', () => {
+            debug('https server ready now');
+            resolve(sslServer);
+        });
+    })
+}
+
+module.exports.httpSslServerStart = httpSslServerStart;

+ 70 - 0
bin/www

@@ -0,0 +1,70 @@
+#!/usr/bin/env node
+
+const http = require('http');
+const https = require('https');
+const debug = require('debug')('ultrong:server');
+const opn = require('opn');
+const path = require('path');
+const fs = require('fs');
+const app = require('../app');
+const net = require('net');
+const httpserver = require('./httpserver');
+
+
+
+const httpport = 7333;
+
+
+
+httpserver.httpServerStart(httpport, app).then(result => {
+  debug('http Listening on ' + httpport);
+  // debug('http NODE_ENV=' + process.env.NODE_ENV);
+}).catch(err => {
+  onError(err);
+});
+
+
+
+function normalizehttpport(val) {
+  let httpport = parseInt(val, 10);
+
+  if (isNaN(httpport)) {
+    // named pipe
+    return val;
+  }
+
+  if (httpport >= 0) {
+    // httpport number
+    return httpport;
+  }
+
+  return false;
+}
+
+/**
+ * Event listener for HTTP server "error" event.
+ */
+
+function onError(error) {
+  if (error.syscall !== 'listen') {
+    throw error;
+  }
+
+  let bind = typeof httpport === 'string' ?
+    'Pipe ' + httpport :
+    'httpport ' + httpport;
+
+  // handle specific listen errors with friendly messages
+  switch (error.code) {
+    case 'EACCES':
+      console.error(bind + ' requires elevated privileges');
+      process.exit(1);
+      break;
+    case 'EADDRINUSE':
+      console.error(bind + ' is already in use');
+      process.exit(1);
+      break;
+    default:
+      throw error;
+  }
+}

+ 35 - 0
build/build.js

@@ -0,0 +1,35 @@
+require('./check-versions')()
+
+process.env.NODE_ENV = 'production'
+
+var ora = require('ora')
+var rm = require('rimraf')
+var path = require('path')
+var chalk = require('chalk')
+var webpack = require('webpack')
+var config = require('../config')
+var webpackConfig = require('./webpack.prod.conf')
+
+var spinner = ora('building for production...')
+spinner.start()
+
+rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
+  if (err) throw err
+  webpack(webpackConfig, function (err, stats) {
+    spinner.stop()
+    if (err) throw err
+    process.stdout.write(stats.toString({
+      colors: true,
+      modules: false,
+      children: false,
+      chunks: false,
+      chunkModules: false
+    }) + '\n\n')
+
+    console.log(chalk.cyan('  Build complete.\n'))
+    console.log(chalk.yellow(
+      '  Tip: built files are meant to be served over an HTTP server.\n' +
+      '  Opening index.html over file:// won\'t work.\n'
+    ))
+  })
+})

+ 48 - 0
build/check-versions.js

@@ -0,0 +1,48 @@
+var chalk = require('chalk')
+var semver = require('semver')
+var packageConfig = require('../package.json')
+var shell = require('shelljs')
+function exec(cmd) {
+  return require('child_process').execSync(cmd).toString().trim()
+}
+
+var versionRequirements = [
+  {
+    name: 'node',
+    currentVersion: semver.clean(process.version),
+    versionRequirement: packageConfig.engines.node
+  },
+]
+
+if (shell.which('npm')) {
+  versionRequirements.push({
+    name: 'npm',
+    currentVersion: exec('npm --version'),
+    versionRequirement: packageConfig.engines.npm
+  })
+}
+
+module.exports = function () {
+  var warnings = []
+  for (var i = 0; i < versionRequirements.length; i++) {
+    var mod = versionRequirements[i]
+    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
+      warnings.push(mod.name + ': ' +
+        chalk.red(mod.currentVersion) + ' should be ' +
+        chalk.green(mod.versionRequirement)
+      )
+    }
+  }
+
+  if (warnings.length) {
+    console.log('')
+    console.log(chalk.yellow('To use this template, you must update following to modules:'))
+    console.log()
+    for (var i = 0; i < warnings.length; i++) {
+      var warning = warnings[i]
+      console.log('  ' + warning)
+    }
+    console.log()
+    process.exit(1)
+  }
+}

+ 9 - 0
build/dev-client.js

@@ -0,0 +1,9 @@
+/* eslint-disable */
+require('eventsource-polyfill')
+var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
+
+hotClient.subscribe(function (event) {
+  if (event.action === 'reload') {
+    window.location.reload()
+  }
+})

+ 89 - 0
build/dev-server.js

@@ -0,0 +1,89 @@
+require('./check-versions')()
+
+var config = require('../config');
+if (!process.env.NODE_ENV) {
+  process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV);
+}
+
+var opn = require('opn')
+var path = require('path')
+var express = require('express')
+var webpack = require('webpack')
+var proxyMiddleware = require('http-proxy-middleware')
+var webpackConfig = require('./webpack.dev.conf')
+
+// default port where dev server listens for incoming traffic
+var port = process.env.PORT || config.dev.port
+// automatically open browser, if not set will be false
+var autoOpenBrowser = !!config.dev.autoOpenBrowser
+// Define HTTP proxies to your custom API backend
+// https://github.com/chimurai/http-proxy-middleware
+var proxyTable = config.dev.proxyTable
+
+var app = express()
+var compiler = webpack(webpackConfig)
+
+var devMiddleware = require('webpack-dev-middleware')(compiler, {
+  publicPath: webpackConfig.output.publicPath,
+  quiet: true
+})
+
+var hotMiddleware = require('webpack-hot-middleware')(compiler, {
+  log: () => {}
+})
+// force page reload when html-webpack-plugin template changes
+compiler.plugin('compilation', function (compilation) {
+  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
+    hotMiddleware.publish({ action: 'reload' })
+    cb()
+  })
+})
+
+// proxy api requests
+Object.keys(proxyTable).forEach(function (context) {
+  var options = proxyTable[context]
+  if (typeof options === 'string') {
+    options = { target: options }
+  }
+  app.use(proxyMiddleware(options.filter || context, options))
+})
+
+// handle fallback for HTML5 history API
+app.use(require('connect-history-api-fallback')())
+
+// serve webpack bundle output
+app.use(devMiddleware)
+
+// enable hot-reload and state-preserving
+// compilation error display
+app.use(hotMiddleware)
+
+// serve pure static assets
+var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
+app.use(staticPath, express.static('./static'))
+
+var uri = 'http://localhost:' + port
+
+var _resolve
+var readyPromise = new Promise(resolve => {
+  _resolve = resolve
+})
+
+console.log('> Starting dev server...')
+devMiddleware.waitUntilValid(() => {
+  console.log('> Listening at ' + uri + '\n')
+  // when env is testing, don't need open it
+  if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
+    opn(uri)
+  }
+  _resolve()
+})
+
+var server = app.listen(port)
+
+module.exports = {
+  ready: readyPromise,
+  close: () => {
+    server.close()
+  }
+}

+ 71 - 0
build/utils.js

@@ -0,0 +1,71 @@
+var path = require('path')
+var config = require('../config')
+var ExtractTextPlugin = require('extract-text-webpack-plugin')
+
+exports.assetsPath = function (_path) {
+  var assetsSubDirectory = process.env.NODE_ENV === 'production'
+    ? config.build.assetsSubDirectory
+    : config.dev.assetsSubDirectory
+  return path.posix.join(assetsSubDirectory, _path)
+}
+
+exports.cssLoaders = function (options) {
+  options = options || {}
+
+  var cssLoader = {
+    loader: 'css-loader',
+    options: {
+      minimize: process.env.NODE_ENV === 'production',
+      sourceMap: options.sourceMap
+    }
+  }
+
+  // generate loader string to be used with extract text plugin
+  function generateLoaders (loader, loaderOptions) {
+    var loaders = [cssLoader]
+    if (loader) {
+      loaders.push({
+        loader: loader + '-loader',
+        options: Object.assign({}, loaderOptions, {
+          sourceMap: options.sourceMap
+        })
+      })
+    }
+
+    // Extract CSS when that option is specified
+    // (which is the case during production build)
+    if (options.extract) {
+      return ExtractTextPlugin.extract({
+        use: loaders,
+        fallback: 'vue-style-loader'
+      })
+    } else {
+      return ['vue-style-loader'].concat(loaders)
+    }
+  }
+
+  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
+  return {
+    css: generateLoaders(),
+    postcss: generateLoaders(),
+    less: generateLoaders('less'),
+    sass: generateLoaders('sass', { indentedSyntax: true }),
+    scss: generateLoaders('sass'),
+    stylus: generateLoaders('stylus'),
+    styl: generateLoaders('stylus')
+  }
+}
+
+// Generate loaders for standalone style files (outside of .vue)
+exports.styleLoaders = function (options) {
+  var output = []
+  var loaders = exports.cssLoaders(options)
+  for (var extension in loaders) {
+    var loader = loaders[extension]
+    output.push({
+      test: new RegExp('\\.' + extension + '$'),
+      use: loader
+    })
+  }
+  return output
+}

+ 12 - 0
build/vue-loader.conf.js

@@ -0,0 +1,12 @@
+var utils = require('./utils')
+var config = require('../config')
+var isProduction = process.env.NODE_ENV === 'production'
+
+module.exports = {
+  loaders: utils.cssLoaders({
+    sourceMap: isProduction
+      ? config.build.productionSourceMap
+      : config.dev.cssSourceMap,
+    extract: isProduction
+  })
+}

+ 58 - 0
build/webpack.base.conf.js

@@ -0,0 +1,58 @@
+var path = require('path')
+var utils = require('./utils')
+var config = require('../config')
+var vueLoaderConfig = require('./vue-loader.conf')
+
+function resolve (dir) {
+  return path.join(__dirname, '..', dir)
+}
+
+module.exports = {
+  entry: {
+    app: './src/main.js'
+  },
+  output: {
+    path: config.build.assetsRoot,
+    filename: '[name].js',
+    publicPath: process.env.NODE_ENV === 'production'
+      ? config.build.assetsPublicPath
+      : config.dev.assetsPublicPath
+  },
+  resolve: {
+    extensions: ['.js', '.vue', '.json'],
+    alias: {
+      'vue$': 'vue/dist/vue.esm.js',
+      '@': resolve('src')
+    }
+  },
+  module: {
+    rules: [
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader',
+        options: vueLoaderConfig
+      },
+      {
+        test: /\.js$/,
+        loader: 'babel-loader',
+        include: [resolve('src'), resolve('test')]
+      },
+      {
+        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('img/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
+        }
+      }
+    ]
+  }
+}

+ 35 - 0
build/webpack.dev.conf.js

@@ -0,0 +1,35 @@
+var utils = require('./utils')
+var webpack = require('webpack')
+var config = require('../config')
+var merge = require('webpack-merge')
+var baseWebpackConfig = require('./webpack.base.conf')
+var HtmlWebpackPlugin = require('html-webpack-plugin')
+var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
+
+// add hot-reload related code to entry chunks
+Object.keys(baseWebpackConfig.entry).forEach(function (name) {
+  baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
+})
+
+module.exports = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
+  },
+  // cheap-module-eval-source-map is faster for development
+  devtool: '#cheap-module-eval-source-map',
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': config.dev.env
+    }),
+    // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
+    new webpack.HotModuleReplacementPlugin(),
+    new webpack.NoEmitOnErrorsPlugin(),
+    // https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: 'index.html',
+      template: 'index.html',
+      inject: true
+    }),
+    new FriendlyErrorsPlugin()
+  ]
+})

+ 120 - 0
build/webpack.prod.conf.js

@@ -0,0 +1,120 @@
+var path = require('path')
+var utils = require('./utils')
+var webpack = require('webpack')
+var config = require('../config')
+var merge = require('webpack-merge')
+var baseWebpackConfig = require('./webpack.base.conf')
+var CopyWebpackPlugin = require('copy-webpack-plugin')
+var HtmlWebpackPlugin = require('html-webpack-plugin')
+var ExtractTextPlugin = require('extract-text-webpack-plugin')
+var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
+
+var env = config.build.env
+
+var webpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({
+      sourceMap: config.build.productionSourceMap,
+      extract: true
+    })
+  },
+  devtool: config.build.productionSourceMap ? '#source-map' : false,
+  output: {
+    path: config.build.assetsRoot,
+    filename: utils.assetsPath('js/[name].[chunkhash].js'),
+    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
+  },
+  plugins: [
+    // http://vuejs.github.io/vue-loader/en/workflow/production.html
+    new webpack.DefinePlugin({
+      'process.env': env
+    }),
+    new webpack.optimize.UglifyJsPlugin({
+      compress: {
+        warnings: false
+      },
+      sourceMap: true
+    }),
+    // extract css into its own file
+    new ExtractTextPlugin({
+      filename: utils.assetsPath('css/[name].[contenthash].css')
+    }),
+    // Compress extracted CSS. We are using this plugin so that possible
+    // duplicated CSS from different components can be deduped.
+    new OptimizeCSSPlugin({
+      cssProcessorOptions: {
+        safe: true
+      }
+    }),
+    // generate dist index.html with correct asset hash for caching.
+    // you can customize output by editing /index.html
+    // see https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: config.build.index,
+      template: 'index.html',
+      inject: true,
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeAttributeQuotes: true
+        // more options:
+        // https://github.com/kangax/html-minifier#options-quick-reference
+      },
+      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+      chunksSortMode: 'dependency'
+    }),
+    // split vendor js into its own file
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'vendor',
+      minChunks: function (module, count) {
+        // any required modules inside node_modules are extracted to vendor
+        return (
+          module.resource &&
+          /\.js$/.test(module.resource) &&
+          module.resource.indexOf(
+            path.join(__dirname, '../node_modules')
+          ) === 0
+        )
+      }
+    }),
+    // extract webpack runtime and module manifest to its own file in order to
+    // prevent vendor hash from being updated whenever app bundle is updated
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'manifest',
+      chunks: ['vendor']
+    }),
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.build.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+if (config.build.productionGzip) {
+  var CompressionWebpackPlugin = require('compression-webpack-plugin')
+
+  webpackConfig.plugins.push(
+    new CompressionWebpackPlugin({
+      asset: '[path].gz[query]',
+      algorithm: 'gzip',
+      test: new RegExp(
+        '\\.(' +
+        config.build.productionGzipExtensions.join('|') +
+        ')$'
+      ),
+      threshold: 10240,
+      minRatio: 0.8
+    })
+  )
+}
+
+if (config.build.bundleAnalyzerReport) {
+  var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
+  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
+}
+
+module.exports = webpackConfig

+ 330 - 0
certificate.js

@@ -0,0 +1,330 @@
+// =============================================================================
+var express = require("express");
+var request = require("request");
+var bcrypt = require("bcryptjs");
+let axios = require("axios");
+var router = express.Router(); // get an instance of the express Router
+const querystring = require("querystring");
+const dateformat = require('./comment/dateformat');
+var mysql = require("./mysql");
+const PDFDocument = require('pdfkit');
+// const PDFDocument = require('@hollandjake/pdfkit-table');
+const fs = require('fs');
+const _mysqlLabor = ["183.36.26.8", "sc_app"]; // 提交的使用用这两个edu數據庫信息
+
+/**
+ * 获取某年的证书设置
+ */
+router.get('/getseeting', async (req, res, next) => {
+    let year = req.query.year || new Date().getFullYear();
+    let data = await mysql.sqlHandler(_mysqlLabor, 'select * from certificate_seeting where year=' + year);
+    if (data && data.length > 0)
+        res.json({
+            status: true,
+            data: data[0]
+        });
+    else
+        res.json({
+            status: false,
+            err: "没有数据"
+        })
+})
+
+async function saveSetting(data) {
+    try {
+        data.daterange_start = dateformat.dateFormat(data.daterange_start);
+        data.daterange_end = dateformat.dateFormat(data.daterange_end);
+        let res = await mysql.sqlHandler(_mysqlLabor, 'select * from certificate_seeting where year=' + data.year);
+        if (res && res.length > 0) {
+            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}`);
+        } else {
+            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})`);
+        }
+    } catch (err) {
+        return false;
+    }
+}
+
+/**
+ * 保存证书设置
+ */
+router.post('/savesetting', async (req, res, next) => {
+    let fromCollection = req.body;
+    let save = await saveSetting(fromCollection);
+    if (save)
+        res.json({ status: true })
+    else
+        res.json({ status: false, err: '保存失败' })
+});
+
+async function saveBack(data) {
+    try {
+        let res = await mysql.sqlHandler(_mysqlLabor, 'select * from certificate_seeting where year=' + data.year);
+        if (res && res.length > 0) {
+            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}`;
+            let save = await mysql.sqlHandler(_mysqlLabor, sqlString);
+            if (save)
+                return {
+                    status: true
+                }
+            else
+                return {
+                    status: false,
+                    err: '保存失败'
+                }
+
+        } else
+            return {
+                status: false,
+                err: '请先保存证书配置'
+            }
+    } catch (err) {
+        console.log('err', err);
+        return false;
+    }
+}
+
+/**
+ * 保存证书信息
+ */
+router.post('/saveback', async (req, res, next) => {
+    let fromCollection = req.body;
+    let save = await saveBack(fromCollection);
+    res.json(save);
+})
+
+/**
+ * 获取当前用户某个年度的课时以及报名的活动列表
+ * @param {*} acid 
+ */
+async function getUserCertCount(year, openid, pageIndex, pageSize) {
+    //获取改年的起始时间
+    let settingObj = await mysql.sqlHandler(_mysqlLabor, `select * from certificate_seeting where year=${year}`);
+    if (settingObj && settingObj.length > 0) {
+        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}`;
+        let applayList = await mysql.sqlHandler(_mysqlLabor, sqlString);
+        let statics = await mysql.sqlHandler(_mysqlLabor, `select sum(classhour) as classhour from activity_apply_const where openid='${openid}' and year=${year}`);
+
+        return {
+            status: true,
+            data: {
+                applayList: applayList,
+                settingObj: settingObj && settingObj.length > 0 ? settingObj[0] : {},
+                statics: statics && statics.length > 0 ? statics[0] : {}
+            }
+        }
+    } else
+        return {
+            status: false,
+            err: '该年度没有初始化证书设置'
+        }
+}
+
+/**
+ * 写入活动课时
+ * @param {*} acid 
+ * @param {*} openid 
+ * @param {*} classhour 
+ * @returns 
+ */
+async function creatUserApplyActClassHour(acid, openid) {
+    //获取当前年
+    let year = new Date().getFullYear();
+    //获取活动内容
+    let actInfo = await mysql.sqlHandler(_mysqlLabor, `select a.* from activity as a where a.acId='${acid}'`);
+    if (actInfo && actInfo.length > 0) {
+        actInfo = actInfo[0];
+        let acYear = new Date(actInfo.create_at).getFullYear();
+        //查询是否已经记录该课时
+        let hasClassHour = await mysql.sqlHandler(_mysqlLabor, `select * from activity_apply_const where acid='${acid}' and openid='${openid}'`);
+        if (hasClassHour && hasClassHour.length > 0)
+            return { status: false, err: '已经记录' };
+        //查询该年的证书课时配置
+        let certInfo = await mysql.sqlHandler(_mysqlLabor, `select * from certificate_seeting where year=${acYear}`);
+        if (certInfo && certInfo.length > 0) {
+            certInfo = certInfo[0];
+            let assclasshour = certInfo.assclasshour;
+            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())}')`);
+            if (insertRes)
+                return { status: true };
+            else
+                return { status: false, err: '保存失败' };
+
+        } else
+            return { status: false, err: '该年度证书配置无效' };
+
+    } else
+        return { status: false, err: '没有该活动' }
+}
+
+/**
+ * 获取用户证书详情
+ */
+router.get('/getusercertdetail', async (req, res, next) => {
+    let fromCollection = req.query;
+    let data = await getUserCertCount(fromCollection.year, fromCollection.openid, fromCollection.pageIndex || 1, fromCollection.pageSize || 20);
+    res.json(data);
+})
+
+async function getStaticsYearForAdmin(year, pageIndex, pageSize, username = "") {
+     let otherWhere = username != '' ? ' and c.username like "%' + username + '%"' : '';
+    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}`;
+    let list = await mysql.sqlHandler(_mysqlLabor, sqlString);
+    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}`);
+    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}`);
+    return {
+        status: true,
+        data: {
+            list: list,
+            count: count && count.length > 0 ? count[0].count : 0,
+            classhour: statics && statics.length > 0 ? statics[0].classhour : 0
+        }
+    }
+}
+
+/**
+ * 获取统计数据
+ */
+router.get('/getstaticsyearforadmin', async (req, res, next) => {
+    let year = req.query.year || new Date().getFullYear();
+    let pageIndex = req.query.pageIndex || 1;
+    let pageSize = req.query.pageSize || 10;
+    let username = req.query.username || '';
+    let data = await getStaticsYearForAdmin(year, pageIndex, pageSize, username);
+    res.json(data);
+})
+
+/**
+ * 生成PDF
+ * @param {*} openid 
+ * @param {*} year 
+ */
+async function creatPdf(openid, year) {
+    // 创建一个 PDF 文档实例
+    const doc = new PDFDocument();
+    process.stdout.setDefaultEncoding('utf8');
+    // 将文档内容通过管道写入一个文件
+    doc.pipe(fs.createWriteStream(`./static/files/pdf-${openid}-${year}.pdf`));
+    // const response = await axios({
+    //     method: 'get',
+    //     url: 'https://teacherapi.cocorobo.cn/teaching-file/static/yym/Rectangle25.png',
+    //     responseType: 'arraybuffer' // 确保我们获取到的是Buffer类型的数据
+    // });
+
+    // const imageBuffer = Buffer.from(response.data, 'binary');
+    // doc.registerFont('main', './msyh.TTF')
+    // doc.moveDown().image(imageBuffer, {
+    //     width: 400,
+    //     align: 'center', // 居中对齐图片
+    //     valign: 'center',
+    //     x: 100,
+    //     y: 100
+    // });
+    let bigTitleSize = 30;
+    let contentSize = 13;
+    let listSize = 12;
+    // 添加标题
+    doc.font('./msyh.TTF').fontSize(bigTitleSize).fillColor('#000000').text('继续教育证明', {
+        paragraphGap: 5,
+        align: 'center'
+    });
+    doc.font('./msyh.TTF').fontSize(contentSize).fillColor('#000000').text('CONTINUING EDUCATION CERTIFICATE', {
+        paragraphGap: 5,
+        align: 'center'
+    });
+
+
+
+    //获取用户数据
+    let user = await mysql.sqlHandler(_mysqlLabor, `select * from user where openid='${openid}'`);
+    let statics = await mysql.sqlHandler(_mysqlLabor, `select sum(classhour) as classhour from activity_apply_const where year=${year} and openid='${openid}'`);
+    if (user && user.length > 0 && statics && statics.length > 0) {
+        user = user[0];
+        let classhour = statics[0].classhour || 0;
+        // 添加段落
+        doc.font('./msyh.TTF').moveDown().fontSize(contentSize).fillColor('#000000').text(`${user.username}老师(${user.schoolName}),于${year}年度期间,积极参加“丽湖职教双创教育国际虚拟教研室”系列教研活动。该教师在该年度学习期间,累计完成继续教育课程(${classhour}学时),具体参与情况如下:`, {
+            indent: contentSize * 2,
+            lineGap: 5,
+            align: 'left'
+        });
+    }
+
+
+    //获取活动数据
+    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`;
+    let list = await mysql.sqlHandler(_mysqlLabor, sqlString);
+
+
+    if (list && list.length > 0) {
+        for (let i = 0; i < list.length; i++) {
+            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}课时`, {
+                indent: 0,
+                lineGap: 4,
+                align: 'left'
+            });
+
+        }
+    }
+    // 链式调用构建表格
+
+
+    //主板单位
+    doc.font('./msyh.TTF').moveDown().moveDown().moveDown().fontSize(contentSize).fillColor('#000000').text('主办单位:', {
+        paragraphGap: 4,
+        lineGap: 3,
+        align: 'right'
+    });
+
+    let dwList = ['深圳职业技术大学工业训练中心(创新创业学院)(代章)', '南京工业职业技术大学创新创业学院', '浙江工贸职业技术学院创业学院', '深圳职业技术大学联合国教科文组织职业教育计划亚非研究与培训中心', '学堂在线'];
+    dwList.forEach(item => {
+        doc.font('./msyh.TTF').fontSize(contentSize).fillColor('#000000').text(item, {
+            paragraphGap: 3,
+            lineGap: 3,
+            align: 'right'
+        });
+    })
+    //日期
+    doc.font('./msyh.TTF').moveDown().fontSize(contentSize).fillColor('#000000').text(`日期:${dateformat.dateFormat(new Date(), "year-month-day")}`, {
+        align: 'right'
+    });
+
+    //盖章
+    const pageWidth = doc.page.width;
+    const pageHeight = doc.page.height;
+    const margins = doc.page.margins;
+
+    const contentWidth = pageWidth - margins.left - margins.right;
+    const contentHeight = pageHeight - margins.top - margins.bottom;
+    doc.moveDown(-5).image('./static/yinzhang2026.png', { x: contentWidth - 30, width: 130 });
+
+    // 结束文档
+    doc.end();
+    return `pdf-${openid}-${year}.pdf`;
+}
+
+router.post('/downloadcert', async (req, res, next) => {
+    let openid = req.body.openid;
+    let year = req.body.year;
+    if (fs.existsSync(`./static/files/pdf-${openid}-${year}.pdf`)) {
+        res.json({
+            status: true,
+            data: `/static/files/pdf-${openid}-${year}.pdf`
+        })
+    } else {
+        let pdfPath = await creatPdf(openid, year);
+        if (pdfPath && pdfPath != '') {
+            res.json({
+                status: true,
+                data: `/static/files/${pdfPath}`
+            })
+        } else
+            res.json({
+                status: false,
+                err: 'pdf生成失败'
+            })
+    }
+})
+
+// creatPdf('oe5Ow63GMRguC3D_jROKKFOWdwHc', '2025');
+router.creatUserApplyActClassHour = creatUserApplyActClassHour;
+module.exports = router;

+ 97 - 0
comment/algos.js

@@ -0,0 +1,97 @@
+const crypto = require('crypto');
+const moment = require('moment');
+const md5 = require('md5');
+const sha1 = require('sha1');
+
+function pad2(n) {
+    return n < 10 ? '0' + n : n
+}
+
+/**
+ * 微信指定的加密算法
+ */
+function encrypt(obj, addtionalStr) {
+    // sort fields alphabetically 
+    let keyArray = [];
+    for (let key in obj) {
+        if (obj.hasOwnProperty(key)) {
+            keyArray.push(key);
+        }
+    }
+
+    keyArray.sort((l, r) => {
+        if (l > r) {
+            return 1;
+        } else if (l < r) {
+            return -1;
+        } else {
+            throw new 'encrypted obj with same key: ' + l;
+        }
+    });
+
+    let rawString = '';
+    keyArray.forEach(key => {
+        rawString += `${key}=${obj[key]}&`;
+    });
+
+    // remove last &
+    rawString = rawString.substring(0, rawString.length - 1);
+
+    if (addtionalStr) {
+        rawString += addtionalStr;
+    }
+
+
+
+    return rawString;
+}
+
+let Base64Handler = {
+    encryption: (str) => {
+        return Buffer(str).toString('base64');
+    },
+    decrypt: (str) => {
+        return Buffer(str, 'base64').toString();
+    },
+    timespan: () => {
+        var date = new Date();
+        return date.getFullYear().toString() + pad2(date.getMonth() + 1) + pad2(date.getDate()) + pad2(date.getHours()) + pad2(date.getMinutes()) + pad2(date.getSeconds());
+    },
+    genNowSeq: () => {
+        return moment().format('YYYYMMDDHHmmssSSS');
+    },
+    getTimeStamp: () => {
+        var date = new Date();
+        return date.getFullYear().toString() + pad2(date.getMonth() + 1) + pad2(date.getDate()) + pad2(date.getHours()) + pad2(date.getMinutes()) + pad2(date.getSeconds());
+    },
+    genNonceStr: (number = 16) => {
+        return crypto.randomBytes(number).toString('hex');
+    },
+    getClientIp: (req) => {
+        let ip = req.ip;
+        if (ip.substr(0, 7) === "::ffff:") {
+            ip = ip.substr(7)
+        }
+
+        return ip;
+    },
+    /**
+     * 生成n位随机数
+     */
+    creatRandomByNumber: (n) => {
+        var rnd = "";
+        for (var i = 0; i < n; i++) {
+            rnd += Math.floor(Math.random() * 10);
+        }
+        return rnd;
+    },
+    encryptMd5: (obj, etcStr) => {
+        return md5(encrypt(obj, etcStr)).toUpperCase();
+    },
+    encryptSha1: (obj, etcStr) => {
+        return sha1(encrypt(obj, etcStr));
+    }
+}
+
+
+module.exports = Base64Handler;

+ 63 - 0
comment/dateformat.js

@@ -0,0 +1,63 @@
+const debug = require('debug')('ultrong:dateformat');
+module.exports = {
+  dateFormat: (datestring, category = 'all') => {
+    if (datestring === undefined || datestring == '')
+      return '';
+    const date = new Date(datestring);
+    const year = date.getFullYear();
+    const month = (parseInt(date.getMonth()) + 1) < 10 ? '0' + (parseInt(date.getMonth()) + 1).toString() : (parseInt(date.getMonth()) + 1);
+    const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
+    const hour = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
+    const minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
+    const seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
+    switch (category) {
+      case 'all':
+        return year + '-' + month + '-' + day + ' ' + hour + ':' + minutes;
+      case 'month-day':
+        return month + '-' + day;
+      case 'year-month-day':
+        return year + '-' + month + '-' + day;
+      case 'month-day hh:mm:ss':
+        return month + ' - ' + day + ' ' + hour + ':' + minutes + ':' + seconds;
+      case 'month-day hh:mm':
+        return month + ' - ' + day + ' ' + hour + ':' + minutes;
+      case 'difference':
+        const newDate = new Date();
+        if (newDate.getFullYear() > year)
+          return (newDate.getFullYear() - year) + '年前';
+        else if (newDate.getMonth() + 1 > month)
+          return ((newDate.getMonth() + 1) - month) + '月前';
+        else if (newDate.getDate() > day)
+          return (newDate.getDate() - day) + '天前';
+        else if (newDate.getHours() > hour)
+          return (newDate.getHours() - hour) + '小时前';
+        else if (newDate.getMinutes() > minutes)
+          return (newDate.getMinutes() - minutes) + '分钟前';
+        else if (newDate.getSeconds() > seconds)
+          return (newDate.getSeconds() - seconds) + '秒前';
+        else
+          return '';
+      default:
+        return year + '-' + month + '-' + day + ' ' + hour + ':' + minutes + ':' + seconds;
+    }
+
+  },
+  getDateString: () => {
+    let date = new Date();
+    let year = date.getFullYear();
+    let month = date.getMonth() < 10 ? '0' + date.getMonth() : date.getMonth();
+    let day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
+    return '_' + year + month + day;
+  },
+  //将日期换算成今天的日期,时间不变
+  getTimeToToday: (date) => {
+    let cdate = new Date();
+    const year = cdate.getFullYear();
+    const month = (parseInt(cdate.getMonth()) + 1) < 10 ? '0' + (parseInt(cdate.getMonth()) + 1).toString() : (parseInt(cdate.getMonth()) + 1);
+    const day = cdate.getDate() < 10 ? '0' + cdate.getDate() : cdate.getDate();
+    const hour = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
+    const minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
+    const seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
+    return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minutes + ':' + seconds);
+  }
+}

BIN
msyh.TTF


+ 40 - 7
mysql.js

@@ -1,4 +1,6 @@
-//参数
+const { resolve } = require('bluebird');
+
+//参数
 var us = {
     mysql: require('mysql'),
     sqle: {}
@@ -13,9 +15,9 @@ us.mysqlconnection = function(host, database) {
             host: host, //数据库地址
             user: "root", //用户名
             password: "cocorobo", //密码
-            // password: "662675", //密码
+            charset: 'utf8mb4_general_ci',
             database: database, //数据库名称
-           
+            
             // port: 20007 //端口
             port: 3306 //提交数据库端口
         }); //连接超时和错误从连
@@ -24,12 +26,43 @@ us.mysqlconnection = function(host, database) {
     return us.sqle[host][database]; //返回连接对象
 }
 
+//直接执行sql语句
+exports.sqlHandler = function (param, sqlString) {
+    return new Promise((resolve, reject) => {
+        if (param.length > 1) {
+            var _mysqlconnection = us.mysqlconnection(param[0], param[1]); //创建连接池
+            _mysqlconnection.getConnection(function (error, connection) { //获取连接
+                if (error) { //连接错误
+                    console.log("连接失败:", error);
+                    reject(error)
+                } else { //连接成功
+                    connection.query(sqlString, function (error, results, fields) { //执行sql语句 
+                        if (results) {
+                            resolve(results)
+                        } else { //执行错误
+                            console.log("sql执行失败", error || "");
+                            reject(error)
+                        }
+                    });
+
+                    connection.release(function (error) { //释放资源
+                        if (error) console.log("连接释放错误", error);
+                    });
+
+                }
+            });
+        } else
+            reject(false);
+
+    });
+}
+
 
 //连接数据库调用
-exports.usselect = function(param, callback) {
+exports.usselect = function (param, callback) {
     if (param.length > 1) {
         var _mysqlconnection = us.mysqlconnection(param[0], param[1]); //创建连接池
-        _mysqlconnection.getConnection(function(error, connection) { //获取连接
+        _mysqlconnection.getConnection(function (error, connection) { //获取连接
             if (error) { //连接错误
                 console.log("连接失败:", error);
             } else { //连接成功
@@ -50,7 +83,7 @@ exports.usselect = function(param, callback) {
                     _sql += param[2] + "();";
                 }
                 // console.log("拼凑的MySQl语句为:", _sql);
-                connection.query(_sql, _param, function(error, results, fields) { //执行sql语句 
+                connection.query(_sql, _param, function (error, results, fields) { //执行sql语句 
                     if (results) {
                         // if (results.pop) {
                         //     results.pop();
@@ -67,7 +100,7 @@ exports.usselect = function(param, callback) {
                     }
                 });
 
-                connection.release(function(error) { //释放资源
+                connection.release(function (error) { //释放资源
                     if (error) console.log("连接释放错误", error);
                 });
 

+ 88 - 433
package.json

@@ -1,438 +1,93 @@
 {
-  "name": "cocoroboLabor",
-  "main": "app.js",
+  "name": "teaching-new",
+  "version": "1.0.0",
+  "description": "wx",
+  "author": "weifan",
+  "private": true,
   "scripts": {
-    "autoStart": "supervisor start app.js",
-    "autoStop": "supervisor stop app.js",
-    "restart": "supervisor restart app.js",
-    "start": "supervisor start app.js",
-    "stop": "supervisor stop app.js",
-    "dev": "supervisor app.js",
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "dev": "set DEBUG=ultrong:* & nodemon ./bin/www",
+    "vuedev": "node build/dev-server.js",
+    "database": "node database_update",
+    "dayreport": "node dayreporttimer",
+    "clientCsalesTotal": "node clientCsalesTotal",
+    "gasservice": "node gasservice",
+    "aishengxingasasync": "node aishengxingasasync",
+    "aiOilTestHandler": "node aiOilTestHandler"
   },
-  "author": "",
-  "license": "BSD-2-Clause",
-  "private": true,
   "dependencies": {
-    "accepts": {
-      "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=",
-      "requires": {
-        "mime-types": "2.1.17",
-        "negotiator": "0.6.1"
-      },
-      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz",
-      "version": "1.3.4"
-    },
-    "array-flatten": {
-      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
-      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
-      "version": "1.1.1"
-    },
-    "axios": "^1.6.0",
-    "bcryptjs": "^2.4.3",
-    "bignumber.js": {
-      "integrity": "sha512-LDXpJKVzEx2/OqNbG9mXBNvHuiRL4PzHCGfnANHMJ+fv68Ads3exDVJeGDJws+AoNEuca93bU3q+S0woeUaCdg==",
-      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.0.4.tgz",
-      "version": "4.0.4"
-    },
-    "body-parser": {
-      "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
-      "requires": {
-        "bytes": "3.0.0",
-        "content-type": "1.0.4",
-        "debug": "2.6.9",
-        "depd": "1.1.1",
-        "http-errors": "1.6.2",
-        "iconv-lite": "0.4.19",
-        "on-finished": "2.3.0",
-        "qs": "6.5.1",
-        "raw-body": "2.3.2",
-        "type-is": "1.6.15"
-      },
-      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
-      "version": "1.18.2"
-    },
-    "bytes": {
-      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
-      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
-      "version": "3.0.0"
-    },
-    "content-disposition": {
-      "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
-      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
-      "version": "0.5.2"
-    },
-    "content-type": {
-      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
-      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
-      "version": "1.0.4"
-    },
-    "cookie": {
-      "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
-      "version": "0.3.1"
-    },
-    "cookie-signature": {
-      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
-      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
-      "version": "1.0.6"
-    },
-    "core-util-is": {
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "version": "1.0.2"
-    },
-    "debug": {
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "requires": {
-        "ms": "2.0.0"
-      },
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "version": "2.6.9"
-    },
-    "depd": {
-      "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=",
-      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
-      "version": "1.1.1"
-    },
-    "destroy": {
-      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
-      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
-      "version": "1.0.4"
-    },
-    "ee-first": {
-      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
-      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-      "version": "1.1.1"
-    },
-    "encodeurl": {
-      "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=",
-      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz",
-      "version": "1.0.1"
-    },
-    "escape-html": {
-      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
-      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-      "version": "1.0.3"
-    },
-    "etag": {
-      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
-      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
-      "version": "1.8.1"
-    },
-    "express": "^4.17.1",
-    "finalhandler": {
-      "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
-      "requires": {
-        "debug": "2.6.9",
-        "encodeurl": "1.0.1",
-        "escape-html": "1.0.3",
-        "on-finished": "2.3.0",
-        "parseurl": "1.3.2",
-        "statuses": "1.3.1",
-        "unpipe": "1.0.0"
-      },
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
-      "version": "1.1.0"
-    },
-    "forwarded": {
-      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
-      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
-      "version": "0.1.2"
-    },
-    "fresh": {
-      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
-      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
-      "version": "0.5.2"
-    },
-    "http-errors": {
-      "dependencies": {
-        "setprototypeof": {
-          "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=",
-          "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
-          "version": "1.0.3"
-        }
-      },
-      "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
-      "requires": {
-        "depd": "1.1.1",
-        "inherits": "2.0.3",
-        "setprototypeof": "1.0.3",
-        "statuses": "1.3.1"
-      },
-      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
-      "version": "1.6.2"
-    },
-    "iconv-lite": {
-      "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
-      "version": "0.4.19"
-    },
-    "inherits": {
-      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-      "version": "2.0.3"
-    },
-    "ipaddr.js": {
-      "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=",
-      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz",
-      "version": "1.5.2"
-    },
-    "isarray": {
-      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "version": "1.0.0"
-    },
-    "jsonwebtoken": "^9.0.2",
-    "loader": "^2.1.1",
-    "media-typer": {
-      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
-      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
-      "version": "0.3.0"
-    },
-    "merge-descriptors": {
-      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
-      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
-      "version": "1.0.1"
-    },
-    "methods": {
-      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
-      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
-      "version": "1.1.2"
-    },
-    "mime": {
-      "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
-      "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
-      "version": "1.4.1"
-    },
-    "mime-db": {
-      "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
-      "version": "1.30.0"
-    },
-    "mime-types": {
-      "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
-      "requires": {
-        "mime-db": "1.30.0"
-      },
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
-      "version": "2.1.17"
-    },
-    "morgan": "^1.9.1",
-    "ms": {
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "version": "2.0.0"
-    },
-    "mssql": {
-      "dependencies": {
-        "generic-pool": {
-          "integrity": "sha1-rwTcLDJc/Ll1Aj+lK/zpYXp0Nf0=",
-          "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz",
-          "version": "2.1.1"
-        },
-        "tedious": {
-          "dependencies": {
-            "big-number": {
-              "integrity": "sha1-4GeVx+bVy1ldysgjb7U1zYyUdyM=",
-              "resolved": "https://registry.npmjs.org/big-number/-/big-number-0.3.0.tgz",
-              "version": "0.3.0"
-            },
-            "iconv-lite": {
-              "integrity": "sha1-r1fhTCzNiyfpRde03gcazNWfALs=",
-              "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.2.tgz",
-              "version": "0.4.2"
-            },
-            "sprintf": {
-              "integrity": "sha1-6JJfyYlOGqaJnpCRx/KhITC3DeU=",
-              "resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.1.tgz",
-              "version": "0.1.1"
-            }
-          },
-          "integrity": "sha1-UjVmIQc5jepgusn96Rk6n7JDZrA=",
-          "requires": {
-            "big-number": "0.3.0",
-            "iconv-lite": "0.4.2",
-            "sprintf": "0.1.1"
-          },
-          "resolved": "https://registry.npmjs.org/tedious/-/tedious-1.8.0.tgz",
-          "version": "1.8.0"
-        }
-      },
-      "integrity": "sha1-kDnbuxnGTzutC2mj0RQ//BLuiho=",
-      "requires": {
-        "generic-pool": "2.1.1",
-        "tedious": "1.8.0"
-      },
-      "resolved": "https://registry.npmjs.org/mssql/-/mssql-1.3.0.tgz",
-      "version": "1.3.0"
-    },
-    "multer": "^1.4.5-lts.1",
-    "mysql": "^2.17.1",
-    "negotiator": {
-      "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=",
-      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
-      "version": "0.6.1"
-    },
-    "node-gyp": "^7.1.2",
-    "node-pre-gyp": "^0.14.0",
-    "nodemon": "^3.0.1",
-    "on-finished": {
-      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
-      "requires": {
-        "ee-first": "1.1.1"
-      },
-      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
-      "version": "2.3.0"
-    },
-    "parseurl": {
-      "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=",
-      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
-      "version": "1.3.2"
-    },
-    "path-to-regexp": {
-      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
-      "version": "0.1.7"
-    },
-    "process-nextick-args": {
-      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
-      "version": "1.0.7"
-    },
-    "proxy-addr": {
-      "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=",
-      "requires": {
-        "forwarded": "0.1.2",
-        "ipaddr.js": "1.5.2"
-      },
-      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz",
-      "version": "2.0.2"
-    },
-    "qs": {
-      "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
-      "version": "6.5.1"
-    },
-    "range-parser": {
-      "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
-      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
-      "version": "1.2.0"
-    },
-    "raw-body": {
-      "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
-      "requires": {
-        "bytes": "3.0.0",
-        "http-errors": "1.6.2",
-        "iconv-lite": "0.4.19",
-        "unpipe": "1.0.0"
-      },
-      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
-      "version": "2.3.2"
-    },
-    "readable-stream": {
-      "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
-      "requires": {
-        "core-util-is": "1.0.2",
-        "inherits": "2.0.3",
-        "isarray": "1.0.0",
-        "process-nextick-args": "1.0.7",
-        "safe-buffer": "5.1.1",
-        "string_decoder": "1.0.3",
-        "util-deprecate": "1.0.2"
-      },
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
-      "version": "2.3.3"
-    },
-    "request": "^2.88.0",
-    "safe-buffer": {
-      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
-      "version": "5.1.1"
-    },
-    "send": {
-      "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==",
-      "requires": {
-        "debug": "2.6.9",
-        "depd": "1.1.1",
-        "destroy": "1.0.4",
-        "encodeurl": "1.0.1",
-        "escape-html": "1.0.3",
-        "etag": "1.8.1",
-        "fresh": "0.5.2",
-        "http-errors": "1.6.2",
-        "mime": "1.4.1",
-        "ms": "2.0.0",
-        "on-finished": "2.3.0",
-        "range-parser": "1.2.0",
-        "statuses": "1.3.1"
-      },
-      "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz",
-      "version": "0.16.1"
-    },
-    "serve-static": {
-      "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==",
-      "requires": {
-        "encodeurl": "1.0.1",
-        "escape-html": "1.0.3",
-        "parseurl": "1.3.2",
-        "send": "0.16.1"
-      },
-      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz",
-      "version": "1.13.1"
-    },
-    "setprototypeof": {
-      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
-      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
-      "version": "1.1.0"
-    },
-    "sqlstring": {
-      "integrity": "sha1-UluKT9Jtb3GqYegipsr5dtMa0qg=",
-      "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.0.tgz",
-      "version": "2.3.0"
-    },
-    "statuses": {
-      "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
-      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
-      "version": "1.3.1"
-    },
-    "string_decoder": {
-      "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
-      "requires": {
-        "safe-buffer": "5.1.1"
-      },
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
-      "version": "1.0.3"
-    },
-    "supervisor": "^0.12.0",
-    "type-is": {
-      "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=",
-      "requires": {
-        "media-typer": "0.3.0",
-        "mime-types": "2.1.17"
-      },
-      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz",
-      "version": "1.6.15"
-    },
-    "unpipe": {
-      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
-      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
-      "version": "1.0.0"
-    },
-    "util-deprecate": {
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "version": "1.0.2"
-    },
-    "utils-merge": {
-      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
-      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
-      "version": "1.0.1"
-    },
-    "vary": {
-      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
-      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
-      "version": "1.1.2"
-    }
-  }
+    "@alicloud/dybaseapi": "^1.0.0",
+    "@alicloud/dysmsapi": "^1.0.0",
+    "@alicloud/mns": "^1.0.0-beta5",
+    "@alicloud/sms-sdk": "^1.0.2",
+    "@hollandjake/pdfkit-table": "^0.2.2",
+    "ali-oss": "^5.2.0",
+    "alipay-sdk": "^3.2.0",
+    "axios": "^1.13.5",
+    "bcryptjs": "^3.0.3",
+    "block": "^0.2.1",
+    "body-parser": "^1.18.1",
+    "body-parser-xml": "^1.1.0",
+    "captchapng": "0.0.1",
+    "cookie-parser": "^1.4.3",
+    "debug": "^2.6.8",
+    "dicer": "^0.2.5",
+    "excel-export": "^0.5.1",
+    "express-handlebars": "^3.0.0",
+    "formidable": "^1.2.1",
+    "helper": "0.0.13",
+    "json-bigint": "^0.2.3",
+    "jsonwebtoken": "^9.0.3",
+    "jsrsasign": "^10.5.27",
+    "kitx": "^1.2.1",
+    "kue": "^0.11.6",
+    "mssql": "^4.0.4",
+    "multer": "^1.3.0",
+    "mysql": "^2.13.0",
+    "node-rsa": "^1.1.1",
+    "node-schedule": "^2.1.1",
+    "nodemon": "^1.11.0",
+    "opn": "^5.1.0",
+    "pdfkit": "^0.17.2",
+    "qr-image": "^3.2.0",
+    "ueditor": "^1.2.3",
+    "underscore": "^1.8.3",
+    "urlencode": "^1.1.0",
+    "vue": "^2.3.4",
+    "vue-router": "^2.3.1",
+    "xlsx": "^0.14.3",
+    "xmlreader": "^0.2.3"
+  },
+  "devDependencies": {
+    "bluebird": "^3.5.0",
+    "body-parser": "~1.17.1",
+    "connect-redis": "^3.3.0",
+    "cookie-parser": "~1.4.3",
+    "debug": "^2.6.8",
+    "express": "~4.15.2",
+    "express-session": "^1.15.3",
+    "hbs": "~4.0.1",
+    "kue": "^0.11.5",
+    "md5": "^2.2.1",
+    "moment": "^2.18.1",
+    "morgan": "~1.8.1",
+    "mysql": "^2.13.0",
+    "pmx": "^1.2.0",
+    "redis": "^2.7.1",
+    "request": "^2.81.0",
+    "request-promise": "^4.2.1",
+    "rotating-file-stream": "^1.2.2",
+    "sequelize": "^3.30.4",
+    "serve-favicon": "~2.4.2",
+    "sha1": "^1.1.1",
+    "xml2js": "^0.4.17"
+  },
+  "engines": {
+    "node": ">= 4.0.0",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not ie <= 8"
+  ]
 }

+ 14 - 3
pbl.js

@@ -10,11 +10,11 @@ var mysql = require("./mysql");
 // const _mysqlLabor = ["123.58.32.151", "sc_app"]; //edu數據庫信息
 // const _mysqluser = ["123.58.32.151", "cocorobouser"]; //用户数据库信息
 
-// const _mysqlLabor = ["127.0.0.1", "sc_app"]; //袁一鸣
+// const _mysqlLabor = ["183.36.26.8", "sc_app"]; //袁一鸣
 // const _mysqlLabor = ["10.3.13.84", "sc_app"]; //袁一鸣
 
 const _mysqlLabor = ["172.16.12.5", "sc_app"]; // 提交的使用用这两个edu數據庫信息
-// const _mysqluser = ["172.16.12.5", "cocorobouser"]; //edu數據庫信息
+const certificate = require("./certificate.js");
 
 var crypto = require("crypto");
 var https = require("https");
@@ -521,7 +521,18 @@ postmysql3 = function (req, res, functionname) {
 		p = Object.values(req.body);
 		p.unshift(_mysqlLabor[0], _mysqlLabor[1], functionname);
 		//執行存儲過程
-		mysql.usselect(p, function (ret) {
+		mysql.usselect(p, async function (ret) {
+			if (functionname == 'insert_Signup') {
+				if (parseInt(ret) > 0) {
+					//判断如果符合条件,则写入课时记录表
+					console.log(req.body);
+					let acid = req.body.acid;
+					let openid = req.body.oid;
+					let insert = await certificate.creatUserApplyActClassHour(acid, openid);
+					console.log('insert', insert);
+				}
+			}
+
 			res.end(JSON.stringify(ret));
 		});
 	}

+ 1 - 0
readme.txt

@@ -0,0 +1 @@
+ 

BIN
static/files/output.pdf


BIN
static/files/pdf-oe5Ow63GMRguC3D_jROKKFOWdwHc-2025.pdf


BIN
static/yinzhang2026.png