diff --git a/src/admin/controllers/game.controller.ts b/src/admin/controllers/game.controller.ts index adfe7f9..62ec0da 100644 --- a/src/admin/controllers/game.controller.ts +++ b/src/admin/controllers/game.controller.ts @@ -1,9 +1,20 @@ import BaseController from '../../common/base.controller' -import { permission, router } from '../../decorators/router' +import { permission, role, router } from '../../decorators/router' import { ZError } from '../../common/ZError' import { Game } from '../../models/content/Game' +import { generateQrFile } from '../../services/File' + class GameController extends BaseController{ + @role('anon') + @router('get /api/test') + async test(req) { + let gameId= '60810dd156af0e8550832a44' + let version = '608117912ff0238a3e607d33' + let shop = 'sa6xtgbmj7' + const { file, url } = await generateQrFile({gameId, version, shop}) + return {file, url} + } @permission(['game:read', 'shop:game_setting']) @router('post /games') async list(req, res) { diff --git a/src/admin/controllers/shop.controller.ts b/src/admin/controllers/shop.controller.ts index dda7aa9..2dd3c9f 100644 --- a/src/admin/controllers/shop.controller.ts +++ b/src/admin/controllers/shop.controller.ts @@ -3,6 +3,7 @@ import { permission, router } from '../../decorators/router' import { Shop } from '../../models/shop/Shop' import { ZError } from '../../common/ZError' import { Game } from '../../models/content/Game' +import { generateQrFile } from '../../services/File' class ShopController extends BaseController { @@ -149,6 +150,15 @@ class ShopController extends BaseController { versionid: shop.gameInfo.versionid } } + + @permission('shop:edit') + @router('post /shop/gameqr') + async getGameQr(req: any) { + let { shop, gameId, version } = req.params + const { url } = await generateQrFile({gameId, version, shop}) + return { url } + } + @permission('shop:edit') @router('post /shop/save_qtype') async updateQTypes(req) { diff --git a/src/models/content/Game.ts b/src/models/content/Game.ts index 37f70a7..10bf0eb 100644 --- a/src/models/content/Game.ts +++ b/src/models/content/Game.ts @@ -25,6 +25,9 @@ export class GameVersion extends Base{ @prop() public appid: string + @prop() + public appsecret: string + @prop() public image: string @@ -61,26 +64,14 @@ class GameClass extends BaseModule { public createdBy: string public static parseQueryParam(params) { - let {key, timeBegin, timeEnd, shop, hasVersion} = params - let opt: any = {deleted: false} - if (key) { - opt.name = {$regex: key, $options: 'i'} - } - if (shop) { - opt.shop = shop - } - if (timeBegin && !timeEnd) { - opt.createdAt = {$gte: timeBegin}; - } else if (timeBegin && timeEnd) { - opt['$and'] = [{createdAt: {$gte: timeBegin}}, {createdAt: {$lte: timeEnd}}]; - } else if (!timeBegin && timeEnd) { - opt.createdAt = {$lte: timeEnd}; + let options: any = { + matchKey: 'name' } + let { opt, sort } = super.parseQueryParam(params, options) + let { hasVersion } = params if (hasVersion) { Object.assign(opt, {'versions.0': {$exists: true}}) } - - let sort = {_id: 1} return { opt, sort } } @@ -88,6 +79,14 @@ class GameClass extends BaseModule { return this.findOne({deleted: false, 'versions.0': {$exists: true} }).exec() } + public static async fetchVersionInfo(gameId: string, version: string) { + let game = await Game.findById(gameId) + if (!game) { + return null + } + return game.versions.find( o => o._id.toHexString() === version) + } + } export const Game = getModelForClass(GameClass, { existingConnection: GameClass.db }) diff --git a/src/services/File.ts b/src/services/File.ts new file mode 100644 index 0000000..3cd0495 --- /dev/null +++ b/src/services/File.ts @@ -0,0 +1,53 @@ +import config from '../config/config' +import * as jetpack from 'fs-jetpack' +import { Game } from '../models/content/Game' +import { ZError } from '../common/ZError' +import { generateQr } from './Wechat' + +export function generateUploadPath(subPath: string) { + const base = config.file.upload_location + const path = `${base}${subPath}` + jetpack.dir(path) + return path +} + +/** + * 生成包含该店铺信息的小程序码 + * @param {string} gameId + * @param {string} version + * @param {string} shop + * @return {{file: string, url: string}} + */ +export async function generateQrFile({gameId, version, shop } : {gameId: string, version: string, shop: string}) { + let subPath = `/qr/${gameId}/${version}` + let path = generateUploadPath(subPath) + let file = `${path}/${shop}.png` + let url = `${config.file.show_url}${subPath}/${shop}.png` + if ( jetpack.exists(file) !== 'file' ) { + let versionData = await Game.fetchVersionInfo(gameId, version) + if (!versionData || !versionData.appid || !versionData.appsecret) { + throw new ZError(20, 'game version not found') + } + await generateQr({ + appId: versionData.appid, + appSecret: versionData.appsecret, + scene: shop, + file + }) + } + return {file, url} +} + +/** + * 检查该游戏包含该店铺信息的小程序码是否存在 + * @param {string} gameId + * @param {string} version + * @param {string} shop + * @return {boolean} + */ +export function checkQrExists(gameId: string, version: string, shop: string) { + let subPath = `/qr/${gameId}/${version}` + let path = generateUploadPath(subPath) + let file = `${path}/${shop}.png` + return jetpack.exists(file) === 'file' +} diff --git a/src/services/Wechat.ts b/src/services/Wechat.ts index ef017dd..39ba647 100644 --- a/src/services/Wechat.ts +++ b/src/services/Wechat.ts @@ -1,5 +1,40 @@ +import axios, { AxiosRequestConfig } from 'axios' +import fs from 'fs' -export async function generateQr({appId, appSecret, scene, filePath}) { - +export async function generateQr({appId, appSecret, scene, file }) { + const stream = fs.createWriteStream(file); + const token = await refreshToken(appId, appSecret) + const url = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${token}` + const reqParams = { + scene: scene, + width: 430, + auto_color: false, + line_color: {'r': '0', 'g': '0', 'b': '0'}, + } + let reqConfig: AxiosRequestConfig = { + method: 'post', + url, + headers: { + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + }, + responseType: 'stream', + data: reqParams + } + const { data } = await axios(reqConfig) + data.pipe(stream) + return true +} + + +export async function refreshToken(appId: string, appSecret: string) { + const link = + `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}` + const { data } = await axios.get(link) + if (!data.errcode) { + return data.access_token + } else { + throw new Error(data.errmsg) + } }