commit
383c2f33c1
@ -0,0 +1,8 @@
|
|||||||
|
class Wechats::JsSdkSignaturesController < ApplicationController
|
||||||
|
def create
|
||||||
|
signature = Util::Wechat.js_sdk_signature(params[:url], params[:noncestr], params[:timestamp])
|
||||||
|
render_ok(signature: signature)
|
||||||
|
rescue Util::Wechat::Error => ex
|
||||||
|
render_error(ex.message)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,74 @@
|
|||||||
|
module Util::Wechat
|
||||||
|
BASE_SITE = 'https://api.weixin.qq.com'.freeze
|
||||||
|
|
||||||
|
Error = Class.new(StandardError)
|
||||||
|
|
||||||
|
class << self
|
||||||
|
attr_accessor :appid, :secret
|
||||||
|
|
||||||
|
def js_sdk_signature(url, noncestr, timestamp)
|
||||||
|
str = { jsapi_ticket: jsapi_ticket, noncestr: noncestr, timestamp: timestamp, url: url }.to_query
|
||||||
|
Digest::SHA1.hexdigest(str)
|
||||||
|
end
|
||||||
|
|
||||||
|
def access_token
|
||||||
|
# 7200s 有效时间
|
||||||
|
Rails.cache.fetch(access_token_cache_key, expires_in: 100.minutes) do
|
||||||
|
result = request(:get, '/cgi-bin/token', appid: appid, secret: secret, grant_type: 'client_credential')
|
||||||
|
result['access_token']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh_access_token
|
||||||
|
Rails.cache.delete(access_token_cache_key)
|
||||||
|
access_token
|
||||||
|
end
|
||||||
|
|
||||||
|
def jsapi_ticket
|
||||||
|
# 7200s 有效时间
|
||||||
|
Rails.cache.fetch(jsapi_ticket_cache_key, expires_in: 100.minutes) do
|
||||||
|
result = request(:get, '/cgi-bin/ticket/getticket', access_token: access_token, type: 'jsapi')
|
||||||
|
result['ticket']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh_jsapi_ticket
|
||||||
|
Rails.cache.delete(jsapi_ticket_cache_key)
|
||||||
|
jsapi_ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
def access_token_cache_key
|
||||||
|
"#{base_cache_key}/access_token"
|
||||||
|
end
|
||||||
|
|
||||||
|
def jsapi_ticket_cache_key
|
||||||
|
"#{base_cache_key}/jsapi_ticket"
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_cache_key
|
||||||
|
"wechat/#{appid}"
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def request(method, url, **params)
|
||||||
|
Rails.logger.error("[wechat] request: #{method} #{url} #{params.inspect}")
|
||||||
|
|
||||||
|
client = Faraday.new(url: BASE_SITE)
|
||||||
|
response = client.public_send(method, url, params)
|
||||||
|
result = JSON.parse(response.body)
|
||||||
|
|
||||||
|
Rails.logger.error("[wechat] response:#{response.status} #{result.inspect}")
|
||||||
|
|
||||||
|
if response.status != 200
|
||||||
|
raise Error, result.inspect
|
||||||
|
end
|
||||||
|
|
||||||
|
if result['errcode'].present? && result['errcode'].to_i.nonzero?
|
||||||
|
raise Error, result.inspect
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,16 @@
|
|||||||
|
wechat_config = {}
|
||||||
|
|
||||||
|
begin
|
||||||
|
config = Rails.application.config_for(:configuration)
|
||||||
|
wechat_config = config['wechat']
|
||||||
|
raise 'wechat config missing' if wechat_config.blank?
|
||||||
|
rescue => ex
|
||||||
|
raise ex if Rails.env.production?
|
||||||
|
|
||||||
|
puts %Q{\033[33m [warning] wechat config or configuration.yml missing,
|
||||||
|
please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m}
|
||||||
|
wechat_config = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
Util::Wechat.appid = wechat_config['appid']
|
||||||
|
Util::Wechat.secret = wechat_config['secret']
|
@ -0,0 +1,54 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
import ExerciseDisplay from '../../../courses/exercise/ExerciseDisplay'
|
||||||
|
|
||||||
|
class ExerciseBanksDetail extends Component{
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount = () =>{
|
||||||
|
|
||||||
|
}
|
||||||
|
detailFetchCallback = (result) => {
|
||||||
|
let Id=this.props.match.params.Id;
|
||||||
|
|
||||||
|
const crumbData={
|
||||||
|
title: result.data.exercise && result.data.exercise.name,
|
||||||
|
is_public: result.data.exercise && result.data.exercise.is_public,
|
||||||
|
crumbArray:[
|
||||||
|
{content:'详情'},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
const menuData={
|
||||||
|
tab:'0',//tab选中的index
|
||||||
|
menuArray:[//tab以及tab路由
|
||||||
|
{to:`/banks/exercise/${Id}`,content:'内容详情'}
|
||||||
|
],
|
||||||
|
category:'exercise',//
|
||||||
|
tos: `/banks/exercise/${Id}/edit`,
|
||||||
|
id: Id,
|
||||||
|
|
||||||
|
}
|
||||||
|
this.props.initPublic(crumbData,menuData);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
let { pollDetail } = this.state
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div>
|
||||||
|
<ExerciseDisplay {...this.props} {...this.state}
|
||||||
|
urlPath = {'exercise_banks'}
|
||||||
|
detailFetchCallback={this.detailFetchCallback}
|
||||||
|
>
|
||||||
|
</ExerciseDisplay>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default ExerciseBanksDetail
|
Loading…
Reference in new issue