我是靠谱客的博主 忧心蜜蜂,这篇文章主要介绍【微信开发】基于微信公众号的早起签到程序0 成果展示1 项目背景与需求分析2 概要设计3 开发环境配置4 编码实现5 参考文献:6 附:style.css文件代码 ,现在分享给大家,希望可以做个参考。

  • 说明:本文讲述基于公众号实现的签到程序的全部开发过程。开发环境:PHP+MySQL。
  • 源码下载地址:敬请期待
  • 关注微信公众号【知行校园汇】可免费下载全部源码。
  • >>点击查看WUTer计算机专业实验汇总
  • 谨记:纸上得来终觉浅,绝知此事要躬行。

0 成果展示

本文较长,为了减少读者的时间,先展示本项目的成果图,以便读者快速确实这篇文章是不是正在寻找的文章。

这里有必要说明一下,项目中的前端页面配色配图等参考自网络公开的CSS样式表。

关于项目的详情,请见后文。

0.1 签到主页面:

用户点击公众号的菜单【早起签到】后,即实现自动微信登录,进入签到主页面。

主页面展示如下:

左图:未到签到时间页面      中图:签到页面      右图:签到完成后显示名次页面

主页面分为三个展示页面。未到签到时间(0:00到05:50期间)显示图如上左图所示。到签到时间时,按钮状态可点击,如上中图。签到完成后,将显示该用户在本公众号的当日签到名次。

0.2 签到记录页

用户签到后,可以查看本用户自己的签到记录。详情如下图所示:

左图:可兑换奖励签到记录和已过期签到记录  右图:已兑换签到记录

在本项目中,用户签到后,可以在本日或者次日的06:50至08:30到指定地点领取奖励。超过这个时间,本条签到记录即作废。三种状态的签到记录如上图所示。

0.3 后台兑换页

下图为后台兑换的测试页面。输入签到ID即可查询该签到记录。

左图:查询已过期签到记录      中图:查询已兑换签到记录      右图:查询可兑换签到记录

如上图。如果前来兑换的用户的签到记录已过期,则显示如上左图所示。如果用户的签到记录已兑换,则显示如上中图所示。如果用户的签到记录可兑换,则显示如上右图所示。

点击“立即兑换”按钮,则显示“兑换成功”弹窗,如下图所示。

兑换成功弹窗(微信Web开发者工具中运行)

看到这里,如果这不是你想要的项目,那么你可以关闭本篇博客了。

如果这是你想要的项目,或者本项目和你当前项目接近,或者想学习这个项目的编写,或者……

请继续往下看。下面正式开始^_^

1 项目背景与需求分析

1.1 项目背景

关于此需求分析部分,先从项目的背景说起吧。

起因是这样的。项目组要在公众号内举办一个活动。这个活动简单易懂,就是“早起签到领奖励”。每天早上指定时间开启签到系统,然后用户点击菜单栏的“早起签到”按钮,即可通过微信登录后,进入签到系统。

用户签到完成后,用户可以凭签到记录到指定地点领取早餐一份。同时每日的早餐限量,所以先到先得。同时签到记录也有兑换期限,本项目组指定的计划是本日或者次日的8:30前均可领取。所以今天签到,后天早上就领不到早餐了^_^

1.2 需求分析

通过对项目背景的分析,本项目的需求有如下几部分:

1、本项目为公众号项目,微信网页开发;

2、实现用户的微信登录授权,以确定用户身份,并实现单日只能签到一次;

3、用户签到后,记录签到时间以及当日签到名次;

4、显示用户签到记录,以及签到记录的状态(可兑换、已兑换、已过期);

5、后台管理员进行兑换确定。

2 概要设计

2.1 开发技术分析

既然是微信网页开发,并且要实现用户的登录,那么就需要学习一下微信的公众号网页授权机制

微信开放平台中有关于这方面的使用说明的介绍,可以通过开发者文档自行学习。链接如下:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

具体而言,网页授权流程分为四步:

1、引导用户进入授权页面同意授权,获取code

2、通过code换取网页授权access_token(与基础支持中的access_token不同)

3、如果需要,开发者可以刷新网页授权access_token,避免过期

4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)

只有通过微信认证的服务号可以使用“网页授权”接口。

如果想用来进行开发测试,可以申请测试账号。申请链接如下:

https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

关于微信网页授权登录的详细过程,将在实践代码中(后边编码实现过程)进行讲解。

————————————————————

然后需要对开发语言进行分析确定。

微信网页可以采用任意Web开发语言实现。

但是结合本项目来说,需要对数据库的数据进行增删改查,所以最终采用的编程语言是PHP

(在此项目前,笔者未接触过PHP开发o(╥﹏╥)o,笔者熟悉的Web开发语言是Java Web。所以编码实现过程。如有不妥之处,多多包涵(*^▽^*))

最后数据库采用MySQL。

2.2 数据库设计

这个项目本身并不大,所以设计的数据库表单也很简单。

首先是用户表。

在本项目中,只需要获取用户对此公众号产生的唯一标识OpenID即可,并不需要获取用户的昵称、城市、头像等其他开放信息。如果需要用户的这些信息,可以对user表进行扩展。

用户表user的字段只有2列。如下图所示:

各个字段含义:

  • userid:用户在本签到系统中的用户ID,即项目中的唯一标识。主键。int型。
  • openid:通过微信授权接口获取的用户在此公众号所产生的唯一标识。int型。

然后是用户签到记录表。signin表单详情如下:

各个字段含义:

  • signid:用户签到记录的唯一标识。主键。int型。
  • userid:用户ID,为表user的外键。int型。
  • signdata:用户签到的时间戳,单位为ms,含义为1970年至今的时间秒数。int型。
  • signmon:签到的月份。int型。
  • signday:签到记录为本月的第几日。int型
  • signhour:签到记录的时。int
  • signmin:签到记录的分。int
  • mingci:本日签到名次(这里请忽略使用拼音表示Ծ‸Ծ主要是因为排名(rank)为SQL语言的关键字)int
  • isused:本签到记录是否已兑换奖品。是:1,否:0。
  • usetime:兑换时间,同为时间戳。(上图中此列拼写错误,可忽略。)

如果你的项目还需要其他表单,可自行设计。

3 开发环境配置

工欲善其事,必先利其器。进行开发之前,首先需要对开发环境等进行配置。

四个用到的软件

3.1 编程环境:

本项目使用PHP语言。所以编译程序使用的是JetBrains PhpStorm

PHPStorm为付费软件。如果你是学生用户,拥有教育邮箱,可以从JetBrains官网授权获取免费版。

3.2 编译环境:

项目编写过程,难免要进行调试。所以这里用到的PHP网页运行环境为phpStudy

此软件为免费软件。具体如何,可从phpStudy官网查询相关文档。

3.3 MySQL数据库可视化:

调试过程,难免需要对数据库中的数据进行核实检查。

笔者使用的MySQL数据库可视化工具为:Navicat for MySQL

此软件为付费软件。

3.4 网页调试:

如何对公众号网页进行调试?微信公众平台的开发者工具栏就给出了Web开发者工具:微信web开发者工具

如上图,点击【web开发者工具】,跳转到绑定开发者微信号页面。这里将开发者的微信号进行绑定。

 然后前往:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 下载微信web开发者工具电脑客户端。

下载安装后,运行后,需要开发者扫描登录才能运行。运行页面如下:

 

3.5 微信公众后台配置

如果直接将编写完成的项目配置到公众号的菜单栏,微信是不认可你这个项目的。

不过,你也不可能在不配置公众号后台的情况下编写出来这个程序O(∩_∩)O

所谓对公众号后台进行配置,就是获取公众号的秘钥,以及将域名添加到公众号接口白名单的过程。

具体如下:

3.5.1 配置域名

项目最终是需要使用域名进行访问的。并且域名必须启用SSL证书(HTTPS协议)

至于如何在PHPStudy中配置SSL证书,笔者前面写过,点击此处查看。

这里讲述的是将域名加入到公众号的“白名单”中

首先进入公众号后台,点击【设置】>>【公众号设置】>>【功能设置】,如下图所示:

点击上图红框中的两个模块的设置,即可将域名添加授权

为了核验你对这个域名拥有所有权,需要将指定的文件上传到域名服务器的目录中进行校验。如下图所示:

具体配置过程,根据上图提示进行配置即可。

微信公众号支持配置2个网页授权域名,和3个JS接口安全域名。

3.5.2 配置IP白名单

本项目需要用到access_token接口,所以需要将最终部署网站的服务器的IP地址配置到IP白名单中!

也就是将域名所解析到的IP地址配置到公众号IP白名单中(具体在域名解析列表中查看)。

点击微信公众平台后台的【开发】>>【基本配置】,即可进行IP白名单修改配置。

点击上图下面红框中的【查看】,即可对IP白名单进行查看与修改。 

3.5.3 获取开发者密码

开发者密码是校验公众号开发者身份的密码,具有极高的安全性。

点击微信公众平台后台的【开发】>>【基本配置】,即可对开发者密码进行重置获取

重要:这里需要将开发者ID(AppID)和开发者密码(AppSecret)记录下来!

重要:这里需要将开发者ID(AppID)和开发者密码(AppSecret)记录下来!

重要:这里需要将开发者ID(AppID)和开发者密码(AppSecret)记录下来!

 

4 编码实现

都说PHP是一门松散的语言。笔者也是第一次接触PHP,并使用PHP来实现这个项目……总体感觉,PHP挺好用(^o^)/~

项目本身就不大,客户端一共2个页面,所以也就不采用什么框架还是MVC开发模式了(下面模仿MVC开发模式)

4.1 模型(Model)层DAO类代码:

首先需要新建一个Dao类,这个类实现的是构造Dao类,以实现与数据库中的数据进行打交道。

通俗的将,就是实现了对数据库的增删改查,所以的SQL语句均在这里执行。

文件名:dao.php

详细代码以及注释如下所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
<?php //模型层,实现与数据库的数据交换 //设置时区 date_default_timezone_set('Asia/Chongqing'); //忽略错误警告 error_reporting(0); Class Dao{ /** * constructor:构造函数 */ function __construct() { //这里换成你自己的数据库信息,如服务器地址127.0.0.1,登录名root,密码123456,数据库名qiandao,端口号3306 $this->conn=mysqli_connect("127.0.0.1","root","123456","qiandao","3306"); mysqli_query($this->conn,"SET NAMES gbk"); } /** * __destruct:析构函数 */ function __destruct() { // TODO: Implement __destruct() method. mysqli_close($this->conn); } /** * 根据用户的OpenID查询该用户是否已登录注册 * 输入:用户OpenID * 返回值:如果注册,返回用户userID,否则,返回0 */ public function getUser($openid){ $sql="SELECT userid FROM user WHERE openid='".$openid."'"; $results = $this->conn->query($sql); if($row = $results->fetch_row()){ return $row[0];//返回用户userID }else{ return 0;//返回0 } } /** * 查询当前数据库中的userID最大值 * 返回:最后注册的用户的userID */ public function getMaxUserID(){ $sql="SELECT MAX(userid) FROM user"; $results = $this->conn->query($sql); if($row = $results->fetch_row()){ return $row[0];//返回用户userID }else{ return 10000;//返回初始值10000 } } /** * 为该用户创建新用户userID * 输入:用户OpenID * 返回:成功true 或者失败 false */ public function createID($userid,$openid){ $sql="INSERT INTO user(userid,openid) VALUES('".$userid."','".$openid."')"; $this->conn->query($sql); } /** * 获取目前签到日(signday)中的排名最大值(rank) * 如果查询为空,说明当日没有签到的用户,返回:0 * 返回:0 或者最大值 */ public function getMaxRank(){ $now=getdate(date("U")); $day = $now[mday]; $sql="SELECT MAX(mingci) FROM signin where signday='".$day."'"; $results = $this->conn->query($sql); if($row = $results->fetch_row()){ //print $row[0]; return $row[0];//返回最大值 }else{ return 0;//返回0 } } /** * 获取当前签到表中签到ID最大值 * 返回:最大签到ID signid */ public function getMaxSignID(){ $sql="SELECT MAX(signid) FROM signin"; $results = $this->conn->query($sql); if($row = $results->fetch_row()){ return $row[0];//返回最大值 }else{ return 10000;//返回0 } } /** * 将签到记录保存至数据库 * 输入:signid userid 签到时间signdata 签到日signday 签到排名rank isuserd默认为0 */ public function saveSign($signid,$userid,$signdata,$signmon,$signday,$signhour,$signmin,$mingci,$isused,$usertime){ $sql="INSERT INTO signin(signid,userid,signdata,signmon,signday,signhour,signmin,mingci,isused,usertime) VALUES('".$signid."','".$userid."','".$signdata."','".$signmon."','".$signday."','".$signhour."','".$signmin."','".$mingci."','".$isused."','".$usertime."')"; try{ $this->conn->query($sql); }catch (Exception $exception){ print $exception->getMessage(); return false; } return true; } /** * 查询指定用户的当日签到记录 */ public function judgesign($userid){ $now=getdate(date("U")); $day = $now[mday]; $sql="SELECT mingci FROM signin WHERE userid='".$userid."' AND signday='".$day."'"; $results = $this->conn->query($sql); if($row = $results->fetch_row()){ return $row[0];//返回本日的签到名次 }else{ return 0;//返回0,表示名次为0,即未签到 } } /** * 查询所有的签到记录 * 返回:结果集 */ public function getSign($userid){ $sql = "SELECT signid,signdata,mingci,isused,usertime,signday,signhour,signmin FROM signin WHERE userid='".$userid."' ORDER BY signdata DESC"; $results = $this->conn->query($sql); return $results;//返回结果集 } /** * 查询指定签到ID的签到信息 */ public function getUserSignInfo($usersignid){ $sql = "SELECT signid,signdata,mingci,isused,usertime,signday,signhour,signmin FROM signin WHERE signid='".$usersignid."'"; $results = $this->conn->query($sql); return $results; } /** * 更新指定签到ID的签到信息(兑换、兑换时间等) */ public function updateSignInfo($usetime,$signid){ $sql = "UPDATE signin SET isused='1',usertime='".$usetime."' WHERE signid='".$signid."'"; try{ $this->conn->query($sql); }catch (Exception $exception){ print $exception->getMessage(); return false; } return true; } }

上面代码中,细节方面就不纠结了,保证正确的情况下,能实现功能即可。其实应该对数据库操作相关的语句进行Try-Catch判断……

4.2 控制(Control)层control类代码:

这个类文件中实现的是从前端获取用户的数据,如时间等信息,控制签到程序,然后将数据发送到模型层,对前端和数据库操作进行控制,起到中介的作用。

文件名:control.php

详细代码及注释如下所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?php include ('dao.php'); session_start(); class control{ /** * 用户登录时执行的操作 * 新用户:分配ID * 旧用户:直接获取ID */ public static function login($openid){ if(empty($openid)) return 0; $dao = new Dao(); $results = $dao->getUser($openid); //新用户:返回0,老用户:返回userID //新用户 if($results === 0){ $userid = $dao->getMaxUserID() + 1 ; //计算新用户ID $dao->createID($userid,$openid); //创建新用户 return $userid; //返回用户userID } //老用户 else{ return $results; //返回用户userID } } /** * 获取当前用户当日是否已经签到,即用户的名次 */ public static function issign(){ $dao = new Dao(); $mingci = $dao->judgesign($_SESSION["userid"]); return $mingci; } /** * 获取当前用户签到记录 */ public static function getMySign(){ $dao = new Dao(); $results = $dao->getSign($_SESSION["userid"]); return $results; } /** * 获取当前指定签到ID的签到信息 */ public static function getSignInfo(){ $dao = new Dao(); $signid = $_SESSION["usersignid"]; $results = $dao->getUserSignInfo($signid); return $results; } /** * 进行兑换 */ public static function duiHuan(){ $usedata = microtime(true);//时间戳 $usersignid = $_SESSION["usersignid"]; $dao = new Dao(); $success = $dao->updateSignInfo($usedata,$usersignid); return $success; } }

4.3 授权接口调用weixin类

下面这篇开发者文档给出了获取用户OpenID,以及用户个人信息的几个步骤:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

建议读者认真阅读开发者文档中的这篇文章,然后再往下看。

4.3.1 获取(或者说是创建)用户授权链接

用户点击公众号菜单栏后,如果用户未登录,需要引导用户前往确认授权页面。

用户点击这个链接,打开的授权页面是这样的:

这里的关键代码如下所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** * 第1步: 获取用户授权code url * @param string $scope 授权作用域:snsapi_base or snsapi_userinfo,这里选择base * @param string $state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值 * @param string $redirect_url 重定向URL * @return string */ public static function createCodeUrl($scope,$state,$redirect_url){ $open_url = 'https://open.weixin.qq.com'; $redirect_url = urlencode($redirect_url);//这里必须对链接进行处理,即连接中的斜线转换成字符 //参考链接示例:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect $url = $open_url.'/connect/oauth2/authorize?appid='.APPID.'&redirect_uri='.$redirect_url.'&response_type=code&scope='.$scope.'&state='.$state.'#wechat_redirect'; return $url; }

return语句前面一行的字符串中的几个变量值说明一下:

  • APPID:这是开发者ID,本文前面3.3.5节记录的就是这个,也是公众号的唯一标识;
  • redirect_uri:这是用户允许公众号获取信息后,继续跳转到的页面;
  • scope:这是公众号应用授权的作用域。这个变量只有两个值:①值为snsapi_base时,只获取用户的OpenID,不获取用户的昵称、头像等信息,如果scope=snsapi_base,则不会出现上图的授权页面,即实现的是静默授权,最多用户的屏幕会显示“正在登录……”几个字。②值为snsapi_userinfo 时,弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注公众号的情况下,只要用户授权,也能获取其信息。
  • state:重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节。这个参数根据项目实际进行赋值。

如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。

在开发者工具中可以清晰的看到这些信息:

其中code的值为换取access_token的票据。每次用户授权后,code值均不同,code只能使用一次,有效期5分钟。

4.3.2 通过code换取网页授权access_token,从Token中读取用户OpenID

这部分的关键代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
/** * 第2步: 获取用户授权access_token * @param type $code 授权时获得code值 * @return type */ public static function getAuthToken($code){ //参考链接示例:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code $url = self::API_URL.'/sns/oauth2/access_token?appid='.APPID.'&secret='.APPSECRET.'&code='.$code.'&grant_type=authorization_code'; $content = curl_get( $url ); $ret = json_decode($content, true ); return self::getResult( $ret ) ? $ret : null; }

这里链接中用到的三个变量的说明如下:

  • APPID:公众号的唯一标识,同上;
  • secret:公众号的appsecret,前面3.5.3节让记下的一串字符;
  • code:授权时跳转的链接中带的参数。

如果函数执行正确,返回的数据为一个JSON数据包,可以简单理解为C++中的结构体。开发者文档的解释如下:

这里边就包含用户的唯一标识:OpenID

在本项目中,进行到这里就结束了。

4.3.3 完整代码

如果你还想继续获取用户的昵称、头像等公开信息,请看完整代码。

各个过程就是请求链接、获取JSON包的过程。

文件名:weixin.class.php

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
<?php //下面是全局变量的几个值 define('APPID','这里换成你自己的APPID');// define('APPSECRET','这里换成你自己的APPSECRET'); //define('open_url','https://open.weixin.qq.com'); class weixin extends wxcommon{ /** * 第1步: 获取用户授权code url * @param string $scope 授权作用域:snsapi_base or snsapi_userinfo,这里选择base * @param string $state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值 * @param string $redirect_url 重定向URL * @return string */ public static function createCodeUrl($scope,$state,$redirect_url){ $open_url = 'https://open.weixin.qq.com'; $redirect_url = urlencode($redirect_url); //参考链接示例:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect $url = $open_url.'/connect/oauth2/authorize?appid='.APPID.'&redirect_uri='.$redirect_url.'&response_type=code&scope='.$scope.'&state='.$state.'#wechat_redirect'; return $url; } /** * 第2步: 获取用户授权access_token * @param type $code 授权时获得code值 * @return type */ public static function getAuthToken($code){ //参考链接示例:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code $url = self::API_URL.'/sns/oauth2/access_token?appid='.APPID.'&secret='.APPSECRET.'&code='.$code.'&grant_type=authorization_code'; $content = curl_get( $url ); $ret = json_decode($content, true ); return self::getResult( $ret ) ? $ret : null; } /** * 第3步:刷新用户授权access_token * @param type $refresh_token 用户刷新access_token * @return type */ public static function refershAuthToken($refresh_token){ //参考链接示例:https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN $url = self::API_URL.'/sns/oauth2/refresh_token?appid='.APPID.'&grant_type=refresh_token&refresh_token='.$refresh_token; $content = curl_get( $url ); $ret = json_decode($content, true ); return self::getResult( $ret ) ? $ret : null; } /** * 第4步: 获取用户基本信息 * @access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同 * @param type $openid 普通用户的标识,对当前公众号唯一 * @param string $lang 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语 * @return type */ public static function getUserInfoByID( $access_token, $openid, $lang='zh_CN' ){ if( !$lang ) $lang = 'zh_CN'; //$access_token = self::getToken(); //参考链接示例:https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN $url = self::API_URL . "/sns/userinfo?access_token={$access_token}&openid={$openid}&lang={$lang}"; $ret = json_decode(curl_get( $url ), true ); return self::getResult( $ret ) ? $ret : null; } } /** * GET方式获取服务器响应 * @param {string} $url * @return {string|boolen} 成功时返回服务器响应内容,失败则返回false */ function curl_get( $url ){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url);; curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); if(!curl_exec($ch)){ error_log( curl_error ( $ch )); $data = ''; } else { $data = curl_multi_getcontent($ch); } curl_close($ch); return $data; } /** *微信通用接口 */ class wxcommon{ const API_URL = 'https://api.weixin.qq.com'; private static $access_token; private static $expries_time = 0; /** * 用于获取AccessToken。如成功返回AccessToken,失败返回false */ public static function getToken(){ if(isset(self::$access_token) && time() < self::$expries_time){ return self::$access_token; } $url = self::API_URL."/cgi-bin/token?grant_type=client_credential&appid=".APPID."&secret=".APPSECRET; $content=curl_get($url); $ret=json_decode($content,true);//{"access_token":"ACCESS_TOKEN","expires_in":7200} if(array_key_exists('errcode',$ret) && $ret['errcode'] != 0){ return false; }else{ self::$access_token = $ret['access_token']; self::$expries_time = time() + intval($ret['expires_in']); return self::$access_token; } } public static function getResult($ret) { if(!is_array($ret) || !array_key_exists('errcode',$ret)){ return $ret; } $errcode = intval($ret['errcode']); if(in_array($errcode, self::$ERRCODE_MAP)){ if($errcode == 0){ return true; } return array('errcode' => $errcode, 'errinfo' => self::$ERRCODE_MAP[$errcode]); } return array('errcode'=>'-2','errinfo'=>'未知错误'); } static $ERRCODE_MAP = array( '-1' => '系统繁忙', '0' => '请求成功', '40001' => '获取access_token时AppSecret错误,或者access_token无效', '40002' => '不合法的凭证类型', '40003' => '不合法的OpenID', '40004' => '不合法的媒体文件类型', '40005' => '不合法的文件类型', '40006' => '不合法的文件大小', '40007' => '不合法的媒体文件id', '40008' => '不合法的消息类型', '40009' => '不合法的图片文件大小', '40010' => '不合法的语音文件大小', '40011' => '不合法的视频文件大小', '40012' => '不合法的缩略图文件大小', '40013' => '不合法的APPID', '40014' => '不合法的access_token', '40015' => '不合法的菜单类型', '40016' => '不合法的按钮个数', '40017' => '不合法的按钮个数', '40018' => '不合法的按钮名字长度', '40019' => '不合法的按钮KEY长度', '40020' => '不合法的按钮URL长度', '40021' => '不合法的菜单版本号', '40022' => '不合法的子菜单级数', '40023' => '不合法的子菜单按钮个数', '40024' => '不合法的子菜单按钮类型', '40025' => '不合法的子菜单按钮名字长度', '40026' => '不合法的子菜单按钮KEY长度', '40027' => '不合法的子菜单按钮URL长度', '40028' => '不合法的自定义菜单使用用户', '40029' => '不合法的oauth_code', '40030' => '不合法的refresh_token', '40031' => '不合法的openid列表', '40032' => '不合法的openid列表长度', '40033' => '不合法的请求字符,不能包含uxxxx格式的字符', '40035' => '不合法的参数', '40038' => '不合法的请求格式', '40039' => '不合法的URL长度', '40050' => '不合法的分组id', '40051' => '分组名字不合法', '41001' => '缺少access_token参数', '41002' => '缺少appid参数', '41003' => '缺少refresh_token参数', '41004' => '缺少secret参数', '41005' => '缺少多媒体文件数据', '41006' => '缺少media_id参数', '41007' => '缺少子菜单数据', '41008' => '缺少oauth code', '41009' => '缺少openid', '42001' => 'access_token超时', '42002' => 'refresh_token超时', '42003' => 'oauth_code超时', '43001' => '需要GET请求', '43002' => '需要POST请求', '43003' => '需要HTTPS请求', '43004' => '需要接收者关注', '43005' => '需要好友关系', '44001' => '多媒体文件为空', '44002' => 'POST的数据包为空', '44003' => '图文消息内容为空', '44004' => '文本消息内容为空', '45001' => '多媒体文件大小超过限制', '45002' => '消息内容超过限制', '45003' => '标题字段超过限制', '45004' => '描述字段超过限制', '45005' => '链接字段超过限制', '45006' => '图片链接字段超过限制', '45007' => '语音播放时间超过限制', '45008' => '图文消息超过限制', '45009' => '接口调用超过限制', '45010' => '创建菜单个数超过限制', '45015' => '回复时间超过限制', '45016' => '系统分组,不允许修改', '45017' => '分组名字过长', '45018' => '分组数量超过上限', '46001' => '不存在媒体数据', '46002' => '不存在的菜单版本', '46003' => '不存在的菜单数据', '46004' => '不存在的用户', '47001' => '解析JSON/XML内容错误', '48001' => 'api功能未授权', '50001' => '用户未授权该api', ); }

上面这个代码是笔者从一本参考书中摘抄的,可以直接拿来使用。

但是需要将前两行的APPID和APPSECRET补充完整

4.4 签到主页面设计

主页面主要是HTML代码,以及加载HTML代码前执行的几段PHP代码。

这里我们要想清楚几个问题:

用户真正能够授权,后台并且能够拿到code的链接长这样:

复制代码
1
2
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect //注:这是微信开发者文档给出的实例链接

但是这样的链接,太长,看起来就不舒服。

我们正常理解的,我们能接受的公众号网页链接,应该是这样的:

复制代码
1
https://wx.yourdomain.com/index.php

所以这就需要在index.php中进行控制。

这里笔者简单说一下笔者的思路

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
if(没有创建客户端到服务端的session["userid"]){ //说明是第一次访问,session中没有userid这个字段 if(GET到了链接中的state值){ //说明已经拿到code //通过code获取Token //解析Token获取openid //将openid与数据库即有数据对比,获取用户userid //获取其他信息 } else{ //跳转到授权链接,就是前面那个很长的链接 } else{ //反之就是已经创建包含userid的session,用于已经登录,则可以获取网页中需要的信息 //获取其他信息 }

所以这里的关键代码,笔者是这样写的:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/** * 首次访问进行登录 */ //从登录链接返回的两个参数:code和state,其中state用来获取用户的OpenID,state用来判断是否是首次打开页面 //如果没有设置全局session变量userID,执行if内函数 if(!isset($_SESSION["userid"])){ if(isset($_GET["state"])){ $token=weixin::getAuthToken($_GET['code']); //根据请求链接获取返回的Token,其中包含access_token $_SESSION["userid"] = control::login($token['openid']); //根据返回的JSON包,将用户的OpenID进行登录验证,并拿到用于的userID,保存到session中。 $_SESSION["issign"] = control::issign(); //拿到用户的登签到名次,未签到为0 }else{ $url = weixin::createCodeUrl("snsapi_base","123","https://wx.XXX.com/index.php"); header("location:$url"); } } else{ $_SESSION["issign"] = control::issign(); //拿到用户的登签到名次issign,未签到为0 }

完整代码,以及代码详解如下:

文件名:index.php

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<?php //设置时区 date_default_timezone_set('Asia/Chongqing'); session_start(); /** * 本项目中使用session全局数组保存用户名,以及用户的签到名次 * 当然也可以改用cookie * */ //加载几个引用的文件 require 'lib/weixin.class.php'; require 'lib/control.php'; /** * 首次访问进行登录 */ //从登录链接返回的两个参数:code和state,其中state用来获取用户的OpenID,state用来判断是否是首次打开页面 //如果没有设置全局session变量userID,执行if内函数 if(!isset($_SESSION["userid"])){ if(isset($_GET["state"])){ $token=weixin::getAuthToken($_GET['code']); //根据请求链接获取返回的Token,其中包含access_token $_SESSION["userid"] = control::login($token['openid']); //根据返回的JSON包,将用户的OpenID进行登录验证,并拿到用于的userID,保存到session中。 $_SESSION["issign"] = control::issign(); //拿到用户的登签到名次,未签到为0 }else{ $url = weixin::createCodeUrl("snsapi_base","123","https://wx.XXX.com/index.php"); header("location:$url"); } } else{ $_SESSION["issign"] = control::issign(); //拿到用户的登签到名次,未签到为0 } //下面是获取用户的信息,没用到…… //$userinfo=weixin::getUserInfoByID($token['access_token'],$token['openid'],'zh_CN');//根据access_Token获取用户的OpenID //$nickname = $userinfo["nickname"]; //$usersex = $userinfo["sex"]; //……………… ?> <!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0,viewport-fit=cover"> <title>早起签到</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <div class="banner2"><img src="images/banner.jpg" class="img-responsive"></div> <?php $now=getdate(date("U")); //当前时间 $hour = $now[hours]; //签到时 $min = $now[minutes]; //签到分 //如果未到签到时间,则不能签到! if($hour< 6 || ($hour==6 && $min<50) ){ ?> <form name="sign" action="sign.php"> <div id="fromBox" class="fromBox"> <button id="btn-qiandao" class="btn3" disabled="true">5:50开启今日签到</button> </div> </form> <?php } //如果用户未签到,显示可签到 else if($_SESSION["issign"]==0) { //if($issign===0) { ?> <form name="sign" action="sign.php"> <div id="fromBox" class="fromBox"> <button id="btn-qiandao" class="btn3">立即签到</button> </div> </form> <?php } //用户已经签到,显示签到名次 else{ ?> <form name="sign"> <div id="fromBox" class="fromBox"> <button id="btn-qiandao" class="btn3" disabled="true">今日签到名次:<?php echo $_SESSION["issign"]; ?> </button> </div> </form> <?php } ?> <p align="right"><a href="mysign.php" style="text-decoration: none">>>我的签到记录  </a></p> <div class="pt10lr10 mt10"> <div class="pline"></div> <div class="prizebox"> <div class="ptitle"><strong>活动详情</strong></div> <div class="" id="demo" style=""> <table style="font-size: 13px;color: #b25d06"> <tr> <td width="6PX" valign="top" align="right">1、</td><td>每日05:50至23:59开启早起签到。</td> </tr> <tr> <td valign="top" align="right">2、</td><td>其他内容……</td> </tr> <tr> <td valign="top" align="right">3、</td><td>其他内容……</td> </tr> </table> <br> </div> </div> </div> <div align="center"> <a style="font-size: 10px;color: #ff820b;text-decoration: none" href="https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzIwODkwOTg5Nw==&scene=124#wechat_redirect">技术支持:@拾年之璐</a> </div> </body>

这个主页面用到的CSS文件,来自网络,篇幅较长,将在文末展示

这里注意上面代码中的“立即签到”按钮所在的form表单中,action是跳转到sign.php文件,这里要注意!

所以需要对sign.php文件进行编写。这个文件,就是个控制页面,以及跳转页面。详细代码如下:

文件名:sign.php

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php header("Content-type:text/html;charset=UTF-8"); error_reporting(0); include('lib/dao.php'); //下面代码完全可以放在control.php文件中实现,然后此处调用! session_start(); $dao = new Dao(); //获取签到的基本信息 $signid = $dao->getMaxSignID()+1;//签到ID $userid=$_SESSION["userid"];//用户ID $signdata = microtime(true);//时间戳 $now=getdate(date("U")); //当前时间 $signmon = $now[mon]; //签到月份 $signday = $now[mday]; //签到日 $signhour = $now[hours]; //签到时 $signmin = $now[minutes]; //签到分 $mingci = $dao->getMaxRank()+1;//签到名次 $isused = 0; //是否兑换 $usertime = 0; //兑换时间 $success = $dao->saveSign($signid,$userid,$signdata,$signmon,$signday,$signhour,$signmin,$mingci,$isused,$usertime); //上面代码完全可以放在control.php文件中实现,然后此处调用! //签到名次保存到session中! //setcookie("issign",$mingci,time()+60*60*24); //有效期24个小时,弃用cookie $_SESSION["issign"] = $mingci; if($success === true) { echo "<script charset='UTF-8' language='javascript' type='text/javascript'> { window.alert('签到成功!')}; setTimeout( window.parent.location.href='index.php',2000); </script>"; //echo "<script> {window.alert('签到成功!')} </script>"; } else{ echo "<script charset='UTF-8'language='javascript' type='text/javascript'> {window.alert('签到失败,请联系管理员!')} ;window.parent.location.href='index.php'</script>"; }

4.5 我的签到记录页面

前面index.php代码中,有一个“我的签到记录”按钮,是个超链接的形式,所跳转的页面是mysign.php。详细代码如下:

文件名:mysign.php

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?php require 'lib/control.php'; $results = control::getMySign();//得到签到记录集合 $exist = false;//是否存在签到记录 //时间戳转换成标准时间格式 function get_microtime_format($time) { if(strstr($time,'.')){ sprintf("%01.3f",$time); //小数点。不足三位补0 list($usec, $sec) = explode(".",$time); $sec = str_pad($sec,3,"0",STR_PAD_RIGHT); //不足3位。右边补0 }else{ $usec = $time; $sec = "000"; } $date = date("Y-m-d H:i:s.x",$usec); return str_replace('x', $sec, $date); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>我的签到记录</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <div id="wrap"> <div class="banner2"><img src="images/banner.jpg"/></div> <div class="tabsbox tabsbox2"> <div class="title1">我的签到记录</div> <?php while($row = $results->fetch_row()) { $exist = true; ?> <div class="Prize"> <p>签到ID:<?php echo $row[0]; ?>    当日签到名次:<?php echo $row[2]; ?></p> <div>签到时间:<?php echo get_microtime_format($row[1]); $now=getdate(date("U")); //当前时间 $nowday = $now[mday]; //当前日 $nowhour = $now[hours]; //当前时 $nowmin = $now[minutes]; //当前分 //1、昨天5.5.到24.00签到的用户,今天8.30前可兑换 //2、今天5.50到8.30之间签到的用户,可在今天8.30前兑换,或者明天兑换 if(($row[3]==0 && $row[5] == ($nowday-1) && $nowhour<=8 && $nowmin<=30) || ($row[3]==0 && $row[5] == $nowday) ){ ?><a style="font-weight: bold;color:green">  可兑换</a></div><?php } else if($row[3]==1 ){ ?><a style="font-weight: bold;color:red">  已兑换</a></div> <div>兑换时间:<?php echo get_microtime_format($row[4]); ?></div> <?php } else{ ?><a style="font-weight: bold;color:orange">  已过期</a></div><?php } ?> </div> <?php } if($exist === false){?> <div class="Prize"> <div align="center">您还没有签到记录,快去签到吧^_^</div> </div> <?php } ?> </div> <a href="index.php" class="btn4" style="text-decoration: none">返回</a> </div> </body> </html>

4.6 后台兑换页面

后台兑换页面就是一个很简单的HTML页面。其中主要由两个PHP文件组成:

主页面文件名:search.php

完整代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<?php require 'lib/control.php'; //测试用 //$_SESSION["usersignid"] = 10005; //↑ 测试用 $exist = false; if(isset($_REQUEST["usersignid"])){ $usersignid2 = $_REQUEST["usersignid"]; $_SESSION["usersignid"] = $usersignid2; } if(isset($_SESSION["usersignid"])){ $result = control::getSignInfo(); } else{ $result = 0; } //时间戳转换 function get_microtime_format($time) { if(strstr($time,'.')){ sprintf("%01.3f",$time); //小数点。不足三位补0 list($usec, $sec) = explode(".",$time); $sec = str_pad($sec,3,"0",STR_PAD_RIGHT); //不足3位。右边补0 }else{ $usec = $time; $sec = "000"; } $date = date("Y-m-d H:i:s.x",$usec); return str_replace('x', $sec, $date); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>后台查询系统</title> <link rel="stylesheet" href="css/style.css"> <style type="text/css"> input{ border: 1px solid #ccc; padding: 10px 0px; border-radius: 5px; /*css3属性IE不支持*/ padding-left:10px; width: 150px; } .btn5{ display:block; border:0px; margin:0rem auto 0% auto; width: 94%; background-color: #ef2122; text-align: center; font-weight: bold; font-size:17px ; color: #fff3f0; border-radius: 10px; } </style> <script type="text/javascript"> function doAction() { var usersignid = document.getElementById("usersignid"); window.location.href = "a.php?usersignid="+ usersignid.value; } </script> </head> <body> <div> <br><br><br> </div> <form action="a.php" method="post"> <table align="center" width="85%"> <tr> <td width="40%"> <input placeholder="请输入签到ID" type="text" tabindex="1" name="usersignid" id="usersignid" required autofocus autocomplete="on"> </td> <td width="40%"> <input class="btn5" type="button" value="立即查询" onclick="doAction()"> </td> </tr> </table> </form> <div> <br><br><br><br> </div> <div id="wrap"> <div class="tabsbox tabsbox2"> <div class="title1">用户签到记录</div> <?php if(isset($_SESSION["usersignid"])){ while($row = $result->fetch_row()) { $exist = true; ?> <div class="Prize"> <p>签到ID:<?php echo $row[0]; ?>    签到名次:<?php echo $row[2]; ?></p> <div>签到时间:<?php echo get_microtime_format($row[1]); $now=getdate(date("U")); //当前时间 $nowday = $now[mday]; //当前日 $nowhour = $now[hours]; //当前时 $nowmin = $now[minutes]; //当前分 //1、昨天5.5.到24.00签到的用户,今天8.30前可兑换 //2、今天5.50到8.30之间签到的用户,可在今天8.30前兑换,或者明天兑换 if(($row[3]==0 && $row[5] == ($nowday-1) && $nowhour<=8 && $nowmin<=30) || ($row[3]==0 && $row[5] == $nowday) ){ ?><a style="font-weight: bold;color:green">  可兑换</a> <br> <br> <a href="duihuan.php" class="btn4" style="text-decoration: none">立即兑换</a> <?php } else if($row[3]==1){ ?><a style="font-weight: bold;color:red">  已兑换</a> <div>兑换时间:<?php echo get_microtime_format($row[4]); ?></div> <?php } else{ ?><a style="font-weight: bold;color:orangered">  已过期</a><?php }?> </div> <?php } } if($exist === false){?> <div class="Prize"> <div align="center">未查询到该签到记录!</div> </div> <?php } ?> </div> </div> </body> </html>

兑换按钮文件名:submit.php

完整代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php header("Content-type:text/html;charset=UTF-8"); require 'lib/control.php'; $success = control::duiHuan(); if($success === true) { echo "<script charset='UTF-8' language='javascript' type='text/javascript'> { window.alert('兑换成功!')}; setTimeout( window.parent.location.href='a.php',2000); </script>"; //echo "<script> {window.alert('签到成功!')} </script>"; } else{ echo "<script charset='UTF-8'language='javascript' type='text/javascript'> {window.alert('兑换失败,请联系管理员!')} ;window.parent.location.href='a.php'</script>"; }

 至此,本项目的关键代码展示完毕。

本文结束。

5 参考文献:

[1] 软件开发技术联盟编著.PHP+MySQL开发实战[M].北京:清华大学出版社.2013.

[2] 刘乃琦,李忠主编.PHP和MySQL Web应用开发[M].北京:人民邮电出版社.2013.
[3] 于荷云编著.PHP+MySQL网站开发全程实例[M].北京:清华大学出版社.2012.
[4] 易伟著.微信公众平台服务号开发 揭秘九大高级接口[M].北京:机械工业出版社.2014.
[5] 席新亮编著.微信公众平台JSSDK开发实战 公众号与HTML5混合模式揭秘[M].北京:电子工业出版社.2015.
[6] 闫小坤,周涛.微信公众平台应用开发实践[M].北京:清华大学出版社.2017.
[7] 闫小坤,周涛著.微信公众平台应用开发从入门到精通[M].北京:清华大学出版社.2015.
[8] 张暑军主编.基于HTML 5的APP开发教程[M].北京:北京理工大学出版社.2016.

6 附:style.css文件代码 

注:此文件来自网络!

文件名:style.css

完整代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
@charset "UTF-8"; h1,h2,h3,h4,h5,h6,span,p,a,.btn,input,select,textarea,div{ font-weight: normal; font-family: "Microsoft YaHei",微软雅黑,"MicrosoftJhengHei",华文细黑,STHeiti,MingLiu,Helvetica Neue, Helvetica, Arial, sans-serif ;} ul,li{list-style: none;margin:0;padding:0;} a{ color: #323232;} a:hover{ color: #323232; text-decoration: none;} .w100{width:100%;} body{background-color:#faca34} /*背景颜色*/ .bodybg{background:#f7f7f7;} .bgbody{background:#eeeff3;} .bg-white{background:#fff;} .bg-red{background:#e3393a;} .bgmainy{background-color:#ffa15c;}/*crm商品管理,页面主题黄色*/ .bgea{background:#EA6846;} .bgf1{background:#F1F2F4;} .bgf7{background:#f7f7f7;} .bgfb{background:#fbfbfb;} .bgf36{background:#ff3366;} .bgf2f1{background:#f2f1f1;} .bgef1e3b{background:#ef1e3b;} /*字体大小*/ .font10{font-size:10px;} .font12{font-size:12px;} .font13{font-size:13px;} .font14{font-size:14px;} .font15{font-size:15px;} .font16{font-size:16px;} .font18{font-size:18px;} .font20{font-size:20px;} .font42{font-size:42px;} .font2m{font-size: 2em;} .font3m{font-size: 3em;} /*字体颜色*/ .text-red{color:#df493b;} .text-white{color:#fff;} .text-grey{color:#ccc;} .text-grey1{color:#9e9ea1;} .text1{color:#50d2c2;} .text-f36{color:#ff3366;} .colorb5{color:#b5b5b5;} .colorstar {color:#ff4444;} .color29{color:#292929;} .text-orange{color:#ffa15c;} .text-blue{color:#41a4e3;} .text-jiangjiu{color:#da5141;}/* 广东酱酒专家 */ .texthidden{text-overflow:ellipsis;white-space: nowrap;overflow: hidden;}/* 1行 */ .rows2{display:-webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical; overflow: hidden;}/* 2行 */ /*高度和行高*/ .h30{height:30px;} .h35{height:35px;} .h43{height:43px;line-height: 43px;} .h48{height:48px;line-height: 48px;} .h50{height:50px;line-height: 50px;} .h70{height:70px;} .lh25{line-height: 25px;} .lh33{line-height: 33px;} .lh34{line-height: 34px;} .lh47{line-height: 47px;} /*边框样式*/ .bordernone{border:none;} .border0{border:none;} .border{border: 1px solid #e8e9eb;} .bordert{border-top: 1px solid #e8e9eb;} .borderr{border-right: 1px solid #e8e9eb;} .borderb{border-bottom: 1px solid #e8e9eb;} .borderl{border-left:1px solid #e8e9eb;} .bordertb{border-top:1px solid #e8e9eb;border-bottom:1px solid #e8e9eb;} .btn-simple{width:100%;border-radius:0;border:none;} .bradius3{border-radius:3px;} .bradius20{border-top-left-radius:20px;border-bottom-left-radius:20px;border-top-right-radius:20px;border-bottom-right-radius:20px;} .bg-success1{background: #66c300;} /*delete*/ .bordertop{border-top: 1px solid #e8e9eb;} .borderright{border-right: 1px solid #e8e9eb;} .borderbottom{border-bottom: 1px solid #e8e9eb;} /* 内外边距 */ .clearMargin{margin:0;} .clearPadding{padding:0;} .clearPtb{padding-top:0px;padding-bottom:0;} .clearMb{margin-bottom:0;} .clearPb{padding-bottom:0;} /* 内边距 */ .padding5{padding:5px;} .padding10{padding:10px; background-color:#FFF; width:90%; margin:0px auto 10px auto} .padding15{padding:15px;} .p15{padding:15px;} .pt10b3{padding-top:10px;padding-bottom:3px;} .ptb5{padding-top: 5px;padding-bottom: 5px;} .ptb6{padding-top: 6px;padding-bottom: 6px;} .ptb10{padding-top:10px;padding-bottom: 10px;} .ptb15{padding-top: 15px;padding-bottom: 15px;} .ptb20{padding-top: 20px;padding-bottom: 20px;} .ptb30{padding-top:30px;padding-bottom:30px;} .plr0{padding-left:0px;padding-right:0px;} .plr10{padding-left:10px;padding-right:10px;} .plr15{padding-left:15px;padding-right:15px;} .plr25{padding-left: 25px;padding-right: 25px;} .pb5{padding-bottom: 5px} .pb10{padding-bottom: 10px} .pb13{padding-bottom: 13px} .pb15{padding-bottom: 15px} .pb20{padding-bottom: 20px} .pb55{padding-bottom: 55px} .pt5{padding-top:5px;} .pt10{padding-top: 10px;} .pt12{padding-top: 12px} .pt15{padding-top: 15px;} .pt20{padding-top: 20px;} .pt25{padding-top: 25px;} .pt42{padding-top: 42px;} .pt5p{padding-top: 5%;} .pr0{padding-right: 0;} .pr5{padding-right: 5px;} .pl0{padding-left:0;} .pl2{padding-left: 2px;} .pl5{padding-left:5px;} .pl10{padding-left:10px;} .pl15{padding-left: 15px;} .pl60{padding-left: 60px;} .-pl20{padding-left:-20px;} .pl12p{padding-left: 12%;} /* 外边距 */ .margin15{margin:15px;} .margin30{margin:30px;} .m30{margin: 30px;} .mt0{margin-top: 0;} .mt1{margin-top: 1px;} .mt5 {margin-top: 5px;} .mt9{margin-top: 9px;} .mt10{margin-top: 10px;} .mt15{margin-top: 15px;} .mt20 {margin-top: 20px;} .mt30{margin-top: 30px;} .mt-1{margin-top:-1px;} .mb0{margin-bottom: 0;} .mb5{margin-bottom: 5px;} .mb10{margin-bottom: 10px;} .mb15{margin-bottom: 15px;} .mb46{margin-bottom:46px;} .mb60{margin-bottom: 60px;} .mr6{margin-right:6px;} .mr20{margin-right:20px;} .ml20{margin-left:20px;} .mtb5{margin-top:5px;margin-bottom:5px;} .mtb10{margin-top: 10px;margin-bottom: 10px;} .mtb15{margin-top: 15px;margin-bottom: 15px;} .mtb20{margin-top: 20px;margin-bottom: 20px;} .mt20b50{margin-top: 20px;margin-bottom: 50px;} .mlr3{margin-right: 3px;margin-left: 3px;} .mlr10{margin-right:10px;margin-left:10px;} .mlr15{margin-left: 15px;margin-right: 15px;} /*签到*/ .maskbox{width:100%;height:100%;background:rgba(0,0,0,0.7);display: none;position: absolute;z-index:1000;top:0;left:0;} .calendar{background:#faca34;padding:0px 15px 0;} .libaolist .bg-red{background:#e60012;} .libaolist .pt2{padding-top:2px;} .libaolist .pt3{padding-top:3px;} .libaolist .btn-lingqu{width:70px;text-align:center;background:#e60012;color:#fff;} .libaolist .btn-disable{width:70px;text-align:center;background:#c9c9c9;color:#fff;} .btn-qiandao{width:160px;height:50px;background:#e60012;border:5px solid #faca34;color:#fff;font-size:18px;font-weight:bolder;border-radius:25px;text-align:center;position:relative;bottom:-20px;} .qdbox{display:none;padding:15px 0;width:250px;border:3px solid #f82729;border-radius:10px;background:#fff;position:fixed;z-index:1001;top:50%;left:50%;margin-top:-113px;margin-left:-120px;} .qdbox .text-green{color:#e60012;} .btn-lottery{width:120px;text-align:center;color:#fff;background:#e60012;font-size:16px;} .calenbox{width:100%;margin:0 auto;background:#faca34;} .calenbox .date{width:14%;text-align:center;background:#fff;border-radius:7px;color:#6a3906;font-weight:bolder;font-size:18px;padding:10px 0;float:left;border-right:1px solid #faca34;border-bottom:1px solid #faca34;} .singer_r_img{display:block;width:114px;height:52px;line-height:45px;background:url(images/sing_week.gif) right 2px no-repeat;vertical-align:middle;*margin-bottom:-10px;text-decoration:none;} .singer_r_img:hover{background-position:right -53px;text-decoration:none;} .singer_r_img span{margin-left:14px;font-size:16px;font-family:'Hiragino Sans GB','Microsoft YaHei',sans-serif !important;font-weight:700;color:#165379;} .singer_r_img.current{background:url(images/sing_sing.gif) no-repeat 0 2px;border:0;text-decoration:none;} .sign table{width:100%;border-collapse: collapse;border-spacing: 0;color: #a46626;font-weight: bold;font-size:20px;} .sign th,.sign td {width: 30px;height: 40px;text-align: center;line-height: 40px;border:1px solid #faca34;border-radius:6px;background:#fff;} .sign th {font-size: 16px;border-radius:6px;background:#fff;} .sign td {color: #404040;vertical-align: middle;border-radius:6px;background:#fff;color: #a46626;} .sign .on {background-color:#f0bc1a;} .calendar_month_next,.calendar_month_prev{width: 34px;height: 40px;cursor: pointer;background:url(images/sign_arrow.png) no-repeat;} .calendar_month_next {float:right;line-height:40px;} .calendar_month_span {display:inline;line-height: 40px;font-size: 16px;color: #a46626;letter-spacing: 2px;font-weight: bold;} .calendar_month_prev {float:left;line-height:40px;} .sign_succ_calendar_title {text-align: center;border-left:1px solid #faca34;border-right:1px solid #faca34;background:#faca34;} .sign_main{border-top:1px solid #faca34;font-family: "Microsoft YaHei",SimHei;} /* 大转盘样式 */ .turbg{background:#e60012;} .banner{display:block;width:90%;margin:-60px auto 0;} .banner .turnplate{display:block;width:100%;position:relative;} .banner .turnplate canvas.item{width:100%;} .banner .turnplate img.pointer{position:absolute;width:31.5%;height:42.5%;left:34.6%;top:23%;} .prizebox{background:#ffffff;margin:-3px 15px 10px;box-shadow:#d9d9d9 0 5px 20px 3px;color:#6a3906;} .pline{height:12px;border-radius:10px;background:#ef2122;} .ptitle{color:#6a3906;font-size:16px;padding:10px 15px;font-size:16px;} .prizebox .ptitle .text-yellow{color:#6a3906;} .prizebox .ptitle .text-red{color:#ff0000;} .prizebox .prizelist{padding:0 15px;} .prizebox .prizelistwrap{height:auto;overflow:scroll;} .pt10lr10{padding:10px 10px 0;} .turRule{padding:0 15px;color:#7d0000;margin-bottom:20px;} .turRule .text-brown{color:#7d0000;} .turRule .line{height:3px;background:#7d0000;margin-top:10px;} .turRule .ball{display:inline-block;width:10px;height:10px;border-radius:5px;position:absolute;background:#7d0000;top:7px;} .turRule .ball1{left:0;} .turRule .ball2{right:0;} .turRule dl{margin-bottom:10px;} .turRule dl dt{margin-bottom:5px;} .turRule dl dt strong{font-size:16px;} .turRule dl span{display:inline-block;width:18px;height:18px;border-radius:9px;background:#faca34;text-align:center;margin-right:5px;} .banner2{ width:100%} .banner2 img{ width:100%} .from{ width:90%; margin:0px auto;} .from input {width: 100%; border: none; font-size:15px; background-color: #fff; border-radius: 0.5rem; padding: 15px 10px; margin-bottom: 5%;} .from > div input {width: 50%; float: left;} .from > div button {width: 42%; float: right; padding: 15px 0px;display: inline-block;} .sorrytext{ display:none; text-align:center; color:#FFF; margin-bottom:10px} .btn2 {border-radius: 0.5rem; background-color: #ffe335; border: none; padding: 15px 0px; color: #d31427; } .btn3{ display:block; border:0px; margin:0rem auto; width: 90%; padding: 17px 0px; background-color: #ef2122; text-align: center; font-weight: bold; font-size:18px; color: #ffffff; border-radius: 0.26666667rem; } a.btn-lingqu2{background:#e60012;color:#fff;} .btn-disable3{background:#c9c9c9;color:#fff;} .banner2{ width:100%; margin:0px auto 5% auto;} .banner2 img{ width:100%;} .tabsbox { width:94%; margin:0px auto 5% auto;font-size: 80%; color: #fff; background-color:#FFF; padding:11% 0% 5% 0%; position:relative; } .tabsbox2{padding:11% 0% 0% 0%;} .tabsbox .title1 {width:160px;height:44px; line-height:44px;background:#e60012;border:5px solid #faca34;color:#fff;font-size:18px;font-weight:bolder;border-radius:25px;text-align:center;position:absolute; left:50%;top:-27px; margin-left:-85px;} .tabsbox p { width:90%; margin:0px auto; line-height:26px; color:#a46626} .btn4{ display:block; border:0px; margin:0rem auto 5% auto; width: 94%; height: 44px; line-height: 44px; background-color: #ef2122; text-align: center; font-weight: bold; font-size:18px ; color: #fff3f0; border-radius: 4px;} .Prize { border-bottom: solid 1px #faca34; padding:3% 2% 3% 2%; } .Prize > div { color: #000; width:90%; margin:0 auto; line-height:40px}

 

 

最后

以上就是忧心蜜蜂最近收集整理的关于【微信开发】基于微信公众号的早起签到程序0 成果展示1 项目背景与需求分析2 概要设计3 开发环境配置4 编码实现5 参考文献:6 附:style.css文件代码 的全部内容,更多相关【微信开发】基于微信公众号内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(89)

评论列表共有 0 条评论

立即
投稿
返回
顶部