on the way

亦余心之所向兮,虽九死其犹未悔!


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

面向对象的设计原则

发表于 2017-11-26 | 分类于 设计模式 | | 阅读次数:

面向对象的设计原则


在面向对象设计中,可维护性的复用是以设计原则为基础的。每一个原则都蕴含一些面向对象设计的思想,可以从不同的角度提升一个软件结构的设计水平。下面简单阐述下各个设计原则的概念。

单一职责原则(Single Responsibility Principle, SRP)

  • 定义:一个类只负责一个功能领域中的相应职责
  • 理解: 类的设计不能太臃肿,避免造成不可复用、维护不便等问题。比如WEB系统MVC分层设计就包含了单一职责原则思想,DAO层可以被复用,也便于维护。

开闭原则(Open-Closed Principle, OCP)

  • 定义:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
  • 理解:抽象化是开闭原则的关键,类的结构设计应具有抽象化的思想,当增加修改时不应大幅度改变原代码结构。java抽象类与接口的概念符合开闭原则,当修改类功能时直接扩展基类就可以。

里氏代换原则(Liskov Substitution Principle, LSP)

  • 定义:所有引用基类(父类)的地方必须能透明地使用其子类的对象。
  • 理解:里氏代换原则是开闭原则的具体实现手段之一。在传递参数、声明类型、定义变量时都可使用里氏代换原则,也就是用基类(抽象类、接口)来定义类型,待到运行时才确定具体子类类型。

依赖倒转原则(Dependency Inversion Principle, DIP)

  • 定义:抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
  • 理解:实现依赖倒转原则时,针对抽象编程。将具体类的对象通过依赖注入具体注入到其他对象中。(感觉与里氏代换原则很像,分不清…这里理解为里氏代换原则是基础,依赖倒转原则为具体实现)

接口隔离原则(Interface Segregation Principle, ISP)

  • 定义:使用多个专门的接口,而不使用单一的总接口。
  • 理解:接口的粒度必须控制得当,太小会使系统中接口暴增,不利于维护。太大则灵活性较差,且违背单一职责原则。

合成复用原则(Composite Reuse Principle, CRP)

  • 定义:尽量使用对象组合,而不是继承来达到复用的目的。
  • 理解:继承的依赖性相对较强,修改时牵一发而动全身。若两个类之间的关系不是严格的类别关系,而是某个点的职责关系,优先考虑使用合成复用原则。

迪米特法则(Law of Demeter, LoD)

  • 定义: 一个软件实体应当尽可能少地与其他实体发生相互作用。
  • 理解:当前类不要与没有直接关系的类进行消息传递。如必要可以使用中间类(Mediator)来达到松耦合的目的。

linux常用命令

发表于 2017-11-15 | 分类于 linux | | 阅读次数:

linux常用命令


基本操作快捷键

  • 登录shell ctrl+shift+f1-f6
  • 退出shell到图形页面 ctrl+shift+f7
  • 命令补全 tab
  • 暂停正在运行的shell程序执行 ctrl+c
  • 注销shell执行(=exit) ctrl+d

基本操作命令行

  • 修改shell支持语言
  • 查看语言编码格式 echo $LANG
  • 修改语言格式为英文 LANG=en_US
  • 切换用户 su
  • 查看日期 date
  • 查看日历 cal
  • 计算器 bc
  • 显示小数位数(默认整数)sclae=n
  • 离开计算器 quit
  • 命令行帮助文档 man+命令行
  • 将数据同步写入硬盘(一般root写入)*sync
  • 关机 shutdown
  • -h 立即关机
  • -r 立即重启
  • -k 只显示消息
  • shutdown -h +10/20:15 ‘we are readying the linux after 10min/ in 20:15’ 在10分钟后(或于20:15)关机,并且在当前登录此机器的用户屏幕前显示信息
  • 文件系统检查 fsck

文件管理命令行

  • 展示所有文件 ls -al
  • 展示特定文件夹下文件 ls -l /../..
  • 改变文件用户组 chgrp groupname dirname/filename
  • 改变文件所有者 chown ownname [:groupname] dirname/filename
  • 改变文件权限 chmod numberList|chatList dirname/filename
  • rwz三种权限对应数字分别为4 2 1,如果将test.text的权限修改为 rwxrxrw,命令也就是 chmod 756 test.text
  • 也可以使用u g o通过+ - =操作符来修改
  • 创建文件 touch dirname/filename

文件目录操作

特殊符号:

当前目录: .
上层目录: ..
前一个工作目录: -
当前账户主目录: ~
account账户主目录: ~ account

相关命令:

  • 切换目录:cd
  • 显示当前目录:pwd
  • 创建目录: mkdir
  • 删除目录:rmdir
  • 移动文件:mv
  • 删除文件:rm
  • 新建文件: touch
  • 赋予文件隐藏属性 : chattr
  • 新增环境变量:PATH=”$PATH”:/dirname
  • 查找执行文件(PATH下):which
  • 查找特定文件: whereis
  • 查找本地文件:locate(可使用updatedb更新下数据文件再查找,比较耗时)
  • 详细检索文件(磁盘检索): find
  • 查看文件系统属性: dumpe2fs
  • 查看当前使用的文件系统: df
  • 查看文件系统详情: du
  • 磁盘操作: fdisk (使用前需用df命令找到文件系统路径)
  • 磁盘格式化: mkfs
  • 挂载命令: mount
  • 检查文件系统:fsck
  • 文件压缩:gzip (.gz)、bzip2(.bz2)
  • 打包压缩(z代表gzip,j代表bzip2)tar -zpcv -f /root/etc.tar.gz /etc
  • 查看压缩文件 tar -ztv -f /root/etc.tar.gz
  • 解压缩到指定文件夹 tar -zxv -f /root/etc.tar.gz -C /tmp

bash相关命令

  • 查看环境变量:env 或 set
  • 取消环境变量 : unset
  • 变量键盘读取 : read
  • 声明变量类型 : declare或typeset
  • 直接写入配置文件: source 或 .

特殊符号

  • 以覆盖的方法将正确的数据输出:1>
  • 以累加的方法将正确的数据输出: 1>>
  • 以覆盖的方法将错误的数据输出:2>
  • 以累加的方法将错误的数据输出:2>>
  • 标准输入源 : <
  • 标准输入源结束标志 : <<

管道命令

  • 分割字符 : cut
  • 匹配对应行:grep
  • 排序:sort
  • 去重 :uniq
  • 统计: wc
  • 截取数据流重定向:tee
  • 替换,删除字符串 : tr
  • 过滤控制字符:col
  • 多文件相关栏位联结:join
  • 多文件相关栏位粘贴:paste
  • tab转空格:expand -t
  • 切割文件 : split -b
  • 一般命令参数代换为管道命令: xargs
  • 常用处理工具:sed,printf, awk

linux VIM编辑器快捷键

发表于 2017-11-09 | 分类于 linux | | 阅读次数:

linux VIM编辑器快捷键

基本操作

  • i 一般模式进入编辑模式
  • esc 退出编辑模式到一般模式
  • :wq 一般模式下退出vim编辑器并保存更改

移动光标

  • h 或 ← 向左移动一个字符
  • j 或 ↓ 向下移动一个字符
  • k 或 ↑ 向上移动一个字符
  • l 或 → 向右移动一个字符

多次操作采用 数字+快捷键,例如向上移动10个字符可用 10+k 快捷键


  • ctrl+f 或 PageDown 向下移动一页 【常用】
  • ctrl+b 或 PageUp 向上移动一页 【常用】
  • ctrl+d 向下移动半页
  • ctrl+u 向上移动半页
  • + 光标移动到非空格符的下一行
  • - 光标移动到非空格符的上一行
  • n[space] 光标向右移动n个距离
  • 0 或 home 光标移动至行首 【常用】
  • $ 或 end 光标移动至行尾 【常用】
  • H 光标移动至当前屏幕区域首行
  • M 光标移动至当前屏幕区域中间行
  • L 光标移动至当前屏幕区域尾行
  • G 光标移动至当前文件的尾行 【常用】
  • nG 光标移动至当前文件的第n行
  • gg 光标移动至当前文件的第1行【常用】

查找与替换

  • /word 向下寻找名为word的字符串【常用】
  • ?word 向上寻找名为word的字符串
  • n 重复前一个查找操作
  • N 反向重复前一个查找操作
  • :n1,n2s/word1/word2/g n1行到n2行将word1替换word2【常用】
  • :1,$s/word1/word2/g第一行到最后一行将word1替换word2【常用】
  • :1,$s/word1/word2/gc第一行到最后一行将word1替换word2,并提示是否操作【常用】

删除与复制粘贴

  • x,X 向后(前)删除一个字符【常用】
  • nx 向后删除n个字符【常用】
  • nx 向后删除n个字符
  • dd 删除光标所在那一整行
  • ndd 删除光标所在下n整行
  • d1G 删除光标所在到第一行的所有数据
  • dG 删除从光标所在到最后一行的所有数据
  • d$ 删除从光标所在处到该行的最后一个字符
  • d0 删除从光标所在处到该行的最前面一个字符
  • yy 复制光标所在的那一行【常用】
  • nyy 复制光标所在的向下n行【常用】
  • y1G 复制光标所在行到第一行的所有数据
  • yG 复制光标所在行到最后一行的所有数据
  • y0 复制光标所在的那个字符到该行行首的所有数据
  • y$ 复制光标所在的那个字符到该行行尾的所有数据
  • p,P 向下粘贴(向上粘贴)【常用】
  • J 将光标所在行与下一行的数据结合成同一行
  • u 复原前一个操作【常用】
  • ctrl+r 重做前一个操作【常用】
  • . 重复前一个操作【常用】
  • :!command 暂时离开 vi到命令行模式下执行 command 的显示结果

编辑模式

进入插入模式(Insert mode):

  • i,I i为从目前光标所在处插入,I为在目前所在行的第一个非空格符处开始插入【常用】
  • a,A a 为从目前光标所在的下一个字符处开始插入,A 为从光标所在行的最后一个字符处
    开始插入【常用】
  • o,O o 为在目前光标所在的下一行处插入新的一行;O 为在目前光标所在处的上一行插入新的一行【常用】

进入替换模式(Replace mode):

  • r,R r 只会替换光标所在的那一个字符一次;R 会一直替换光标所在的文字,直到按下
    [Esc]键为止【常用】

通用excel导出demo

发表于 2017-05-03 | 分类于 java | | 阅读次数:

通用excel导出demo(利用反射)

最近项目后台管理系统好多地方都要用到excel导出。考虑到一次次修改原代码太无聊,各方面拼拼凑凑写出个通用类,传几个参数设定下表头就ok了。对反射的概念和相关api了解又加深了点…代码记录如下:

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
import org.apache.poi.hssf.usermodel.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class PoiTool {
/**
*
* @param list 数据源list
* @param beanClass list中Object的Class对象
* @param map 表头名与对应字段集合(需预定义,例{<用户名,username><密码,password>})
* @param response 响应对象
* @throws IllegalAccessException
* @throws InstantiationException
* @throws InvocationTargetException
*/
public static void exportExcel(List list, Class beanClass, LinkedHashMap<String, String> map, HttpServletResponse response)
throws IllegalAccessException, InstantiationException, InvocationTargetException {
int j = 0;
String valueStr = "";
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("sheet1");
HSSFCellStyle style = wb.createCellStyle();//样式
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
HSSFRow row = sheet.createRow(0);//第一行
for (Map.Entry<String, String> m : map.entrySet()) {//导入excel表头
HSSFCell cell = row.createCell(j);
cell.setCellValue(m.getKey());
cell.setCellStyle(style);
sheet.autoSizeColumn(j);
valueStr += m.getValue() + "|";//filed字符串
j++;
}
valueStr = valueStr.substring(0, valueStr.length() - 1);
System.out.println(valueStr);
String[] valueArray = valueStr.split("\\|");//filed数组
//通过反射获取对应beanClass中的字段
//并将其转为get方法置入集合中
Field field[] = beanClass.getDeclaredFields();
List<Method> methodlist = new ArrayList<>();
for (int i = 0; i < field.length; i++) {
String fieldName = field[i].getName();
StringBuffer methodName = new StringBuffer("get");
methodName.append(fieldName.substring(0, 1).toUpperCase());
methodName.append(fieldName.substring(1));
Method getMethod = null;
try {
getMethod = beanClass.getMethod(methodName.toString());
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
methodlist.add(getMethod);
}
//遍历list,并插入数据
for (int i = 0; i < list.size(); i++) {
row = sheet.createRow(i + 1);//创建行,从第二行开始
for (int k = 0; k < valueArray.length; k++) {//遍历column
Method getMethod = getCurrentMethod(valueArray[k], methodlist);//获取colunm对应的get方法
if (getMethod.getReturnType().toString().indexOf("int") >= 0) {
row.createCell(k).setCellValue((int) getMethod.invoke(list.get(i), new Object[]{}));
} else if (getMethod.getReturnType().toString().indexOf("String") >= 0) {
row.createCell(k).setCellValue((String) getMethod.invoke(list.get(i), new Object[]{}));
} else if (getMethod.getReturnType().toString().indexOf("double") >= 0) {
row.createCell(k).setCellValue((double) getMethod.invoke(list.get(i), new Object[]{}));
} else if (getMethod.getReturnType().toString().indexOf("float") >= 0) {
row.createCell(k).setCellValue((float) getMethod.invoke(list.get(i), new Object[]{}));
}
}
sheet.autoSizeColumn(i);
}
response.reset();
response.setContentType("application/vnd.ms-excel;charset=gb2312");
response.setHeader("Content-disposition", "attachment;filename=excel.xls");
try {
OutputStream outputStream = response.getOutputStream();
wb.write(outputStream);
wb.close();
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//获取当前column的get方法
public static Method getCurrentMethod(String valueArray, List<Method> methodlist) {
if (valueArray != null && methodlist.size() > 0) {
for (int i = 0; i < methodlist.size(); i++) {
if (methodName(methodlist.get(i).getName()).equals(valueArray)) {
return methodlist.get(i);
}
}
}
return null;
}
//处理methodName
public static String methodName(String str) {
if (str != null) {
str = str.substring(3).substring(0, 1).toLowerCase() + str.substring(3).substring(1);
return str;
}
return null;
}
}

微信公众平台普通红包发放

发表于 2017-04-25 | 分类于 java | | 阅读次数:

业务需求:在后台管理系统为微信用户发放红包,弄了好几天总算搞定了,需要注意的是获取签名部分,流程大体如下:

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
//红包业务方法
public int sendRedpack(HttpServletRequest request,UserAverage userAverage) throws Exception {
WechatInfo wechat = getWechatInfo(request);
WechatInfo wechatInfo = wechatInfoService.queryWechatinfo(wechat.getAppid());
String sendName =wechatInfo.getWxname();
//微信证书路径
String certPath =this.getClass().getResource("/").getPath()+"xxxxx/apiclient_cert.p12";
certPath = URLDecoder.decode(certPath,"UTF-8");//证书路径解码
logger.info("证书路径:"+certPath);
//商户id
String wxappid = wechatInfo.getAppid();
// 微信支付分配的商户号
String partner = wechatInfo.getPartnerid();
//API密钥
String paternerKey =wechatInfo.getPartnerkey();
/**
* 发送普通红包
*/
boolean isSend = sendredpack(wechatInfo,request, "100", "1", "恭喜您获得竞赛奖金!",
"竞赛答题活动", "竞赛奖金", userAverage.getOpenid(),
partner, wxappid, sendName, paternerKey, certPath);
if(!isSend){
return 0;
}
return 1;
}
// 主封装方法
// * @param request 获取IP
// * @param total_amount 付款现金(单位分)
// * @param total_num 红包发放总人数
// * @param wishing 红包祝福语
// * @param act_name 活动名称
// * @param remark 备注
// * @param reOpenid 用户openid
// * @param partner 商户号
// * @param wxappid 公众账号appid
// * @param sendName 商户名称
// * @param paternerKey 商户签名key
// * @param certPath 证书路径
// * @return
// */
public static boolean sendredpack(WechatInfo wechatInfo,HttpServletRequest request,String total_amount,String total_num,String wishing,String act_name,String remark,String reOpenid,String partner,String wxappid,String sendName,String paternerKey,String certPath) throws Exception{
// 商户订单号
String mchBillno = RedPackUtil.createBillNo(wechatInfo);
String ip ="xxx.xx.xxx.xx";//服务器IP
Map<String, String> params = new HashMap<String, String>();
// 随机字符串
params.put("nonce_str", RedPackUtil.createNonce_str());
// 商户订单号
params.put("mch_billno", mchBillno);
// 商户号
params.put("mch_id", partner);
// 公众账号ID
params.put("wxappid", wxappid);
// 商户名称
params.put("send_name", sendName);
// 用户OPENID
params.put("re_openid", reOpenid);
// 付款现金(单位分)
params.put("total_amount", total_amount);
// 红包发放总人数
params.put("total_num", total_num);
// 红包祝福语
params.put("wishing", wishing);
// 终端IP
params.put("client_ip", ip);
// 活动名称
params.put("act_name", act_name);
// 备注
params.put("remark", remark);
//创建签名
String sign = RedPackUtil.redSignal(params, paternerKey);
logger.info("md5签名:"+sign);
params.put("sign", sign);
String xmlResult = RedPackUtil.sendRedPack(params, certPath, partner);//执行发送红包
logger.info("发送红包返回结果:"+xmlResult.toString());
Map<String, String> result = XMLUtil.xml2Map(xmlResult);
logger.info("红包参数:"+result.toString());
//此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
String return_code = result.get("return_code");
//业务结果
String result_code = result.get("result_code");
if (StringUtils.isNotBlank(return_code) && "SUCCESS".equals(return_code)) {
logger.info("通信成功return_code:"+return_code);
if (StringUtils.isNotBlank(result_code) && "SUCCESS".equals(result_code)) {
logger.info("发送成功result_code:"+result_code);
return true;
}else{
logger.info("发送失败result_code:"+result_code);
}
}else{
logger.info("通信失败return_code:"+return_code);
}
return false;
}
/**
* 生成商户订单号
* @param mch_id 商户号
* @param userId 该用户的userID
* @return
*/
public static String createBillNo(WechatInfo wechatInfo){
//组成: mch_id+yyyymmdd+10位一天内不能重复的数字
//10位一天内不能重复的数字实现方法如下:
//因为每个用户绑定了userId,他们的userId不同,加上随机生成的(10-length(userId))可保证这10位数字不一样
Date dt=new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyymmdd");
String nowTime= df.format(dt);
int length = 10 ;
return wechatInfo.getPartnerid() + nowTime + getRandomNum(length);
}
/**
* 生成红包参数随机数nonce_str
* @return
*/
public static String createNonce_str(){
return System.currentTimeMillis()/1000+getRandomNum(5);
}
/**
* 生成特定位数的随机数字
* @param length
* @return
*/
public static String getRandomNum(int length) {
String val = "";
Random random = new Random();
for (int i = 0; i < length; i++) {
val += String.valueOf(random.nextInt(10));
}
return val;
}
/**
* @Title: redSignal
* @Description: 发送红包签名生成
* @param @param params
* @param @return 设定文件
* @return String 返回类型
* @throws
*/
public static String redSignal(Map<String, String> params,String paternerKey) {
SortedMap<String, String> packageParams = new TreeMap<String, String>();
for (Map.Entry<String, String> m : params.entrySet()) {
packageParams.put(m.getKey(), m.getValue().toString());
}
StringBuffer sb = new StringBuffer();
Set<?> es = packageParams.entrySet();
Iterator<?> it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (!StringUtils.isEmpty(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + paternerKey);
System.out.println("未加密签名:"+sb.toString());
String sign = MD5Util.MD5Encode(sb.toString(),"UTF-8").toUpperCase();
return sign;
}
private static String sendRedPackUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
/**
* 发送红包
* @param params 请求参数
* @param certPath 证书文件目录
* @param partner 证书密码
* @return {String}
* @throws Exception
*/
public static String sendRedPack(Map<String, String> params, String certPath, String partner) throws Exception {
return doSend(sendRedPackUrl, XMLUtil.map2Xmlstring(params), certPath, partner);
}
public static String doSend(String url, String data , String certPath , String partner) throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File(certPath));//P12文件目录
try {
keyStore.load(instream, partner.toCharArray());//这里写密码..默认是你的MCHID
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, partner.toCharArray())//这里也是写密码的
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
HttpPost httpost = new HttpPost(url); // 设置响应头信息
httpost.addHeader("Connection", "keep-alive");
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.addHeader("Host", "api.mch.weixin.qq.com");
httpost.addHeader("X-Requested-With", "XMLHttpRequest");
httpost.addHeader("Cache-Control", "max-age=0");
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpost);
try {
HttpEntity entity = response.getEntity();
String jsonStr = toStringInfo(response.getEntity(),"UTF-8");
EntityUtils.consume(entity);
return jsonStr;
} finally {
response.close();
}
} finally {
httpclient.close();
}
}

微信小程序支付接口二次签名

发表于 2017-04-12 | 分类于 java | | 阅读次数:

今天小程序的支付接口遇到签名错误问题。折腾半天…原因是:小程序支付的签名需要进行2次!!!不是成功获取订单后微信服务器回调的xml中的那个sign。

接口返回下列参数供小程序调用

1
2
3
4
paySign //签名 prePay.getMiniPackage(prepayid));
timeStamp //时间戳 prePay.getTimeStamp());
nonceStr//随机签名 prePay.getNonce_str());
package//预支付订单,格式为prepay_id=*;

其中主要参数 paySign,是二次签名。具体签名参数 是采用 调用微信接口获取到预支付订单prepayid后服务器返回的参数(包括小程序需要调用的nonceStr,prepay_id), 主要用下列参数生成paySign,语法如下:

1
appId=【appid】&nonceStr=【随机字符串】&package=prepay_id=【返回的prepay_id】&signType=MD5&timeStamp=【时间戳】&key=【支付秘钥】

二次签名代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public String getMiniPackage(String prepay_id) {
TreeMap<String, String> treeMap = new TreeMap<String, String>();
String timeStamp=OrderUtil.GetTimestamp();
setTimeStamp(timeStamp);
treeMap.put("appId", this.appid);
treeMap.put("nonceStr", getNonce_str());
treeMap.put("package", "prepay_id="+prepay_id);
treeMap.put("signType", "MD5");
treeMap.put("timeStamp",timeStamp);
StringBuilder sb = new StringBuilder();
for (String key : treeMap.keySet()) {
sb.append(key).append("=").append(treeMap.get(key)).append("&");
}
sb.append("key=" + partnerKey);
System.out.println("小程序二次签名:"+sb.toString());
sign = MD5Util.MD5Encode(sb.toString(), "utf-8").toUpperCase();
return sign;
}

mysql基础概念与语法

发表于 2016-05-23 | 分类于 database | | 阅读次数:

由于没有系统学过mysql,之前自己看的相关ppt资料也是囫囵吞枣式的,对视图、游标、事物什么的理解都不是很深刻。特意找了本基础的书(《mysql必知必会》)看了下。


常用命令关键词

登录账户:mysql -u username
显示库的信息:show databases
操作库之前选择对应的库:use database_name
显示库中的表: show tables
显示表的列: show columns from table_name 或 describe table_name
正则表达式查找: REGEXP (用法同LIKE,只不过关键词右边跟的是正则串)


mysql函数

字符串

字符串连接: concat(str…)
串左边字符: left(str,len)
串右边字符: right(str,len)
串长度:length(str)
字串位置 : locate(substr,str)
将串转小写:lower(str)
将串转大写:upper(str)
去全部空格:trim(str)
去左边空格:ltrim(str)
去右边空格:rtrim(str)
返回子串:substirng(str FROM pos FOR len)
soundex发音值:soundex(str)

日期

增加时间:Date_add(date,interval expr type)
当前日期+时间:now()
当前日期:curdate()
当前时间:curtime()
返回日期部分:Date(expr)
返回时间部分:Time(expr)
返回年份部分:year(expr)
返回月份部分:month(expr)
返回天数部分:day(expr)
返回分钟部分:month(expr)
返回秒数部分:second(expr)
返回日期之差(result_type:day):dateDiff(expr1,expr2)
时间格式化:date_format(date,format)
返回星期几:dayOfWeek(date) [1=星期日,2=星期一…7=星期六]

全文本查找

需要为被搜索列建立FULLTEXT索引,并且引擎为MYISAM; 建表语句例如:

1
2
3
4
5
6
7
8
CREATE TABLE test
(
id int NOT NULL AUTO_INCREMENT,
msg text NULL,
PRIMARY KEY (id),
FULLTEXT(msg)
)ENGINE=MYISAM;

文本查找语句则为:

1
select msg from test where Match(msg) against("xiaoqiang");


视图

主要用来简化复杂查询语句,创建可重用的 “sql查询接口”,至于更新操作(包括插入,删除)则取决于视图建立条件,如果采用聚合函数、group by等就不允许更新。(更新视图实质上是对原表进行更新,不确定具体字段 自然不会允许更新)

创建视图:

1
CREATE VIEW `exam_view` AS select * from `exam` where appid = 'xxxxxxx';


存储过程

实质上是sql中的函数,主要用于处理复杂的sql业务(高效,安全)。例如用户商品下单时对库存商品进行一系列处理,检查商品,预定商品,更新库存数目等等 一系列流程就可采用存储过程实现。

创建过程中IN代表函数接收参数,OUT是返回结果,INOUT则复合了IN、OUT的特性。

显示存储过程信息:

1
2
SHOW CREATE PROCEDURE processName;
SHOW PROCEDURE STATUS [LIKE processName];

创建存储过程:

1
2
3
4
CREATE PROCEDURE queryEnameByEid(IN eid_Para INT,OUT ename_result VARCHAR(100))
BEGIN
SELECT ename FROM exam WHERE eid=eid_Para INTO ename_result;
END;

执行存储过程:

1
CALL processName(30,@ename_result);

调用函数结果:

1
SELECT @ename_result;

游标

实质上是存储了数据序列的结果集,在mysql中只能用于存储过程中

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
BEGIN
-- 声明局部变量
DECLARE flag INT DEFAULT 0;
DECLARE tmp_value VARCHAR(50);
-- 声明游标
DECLARE my_cursor CURSOR FOR (SELECT ename FROM exam);
-- 建表
CREATE TABLE IF NOT EXISTS test_cursor(id int AUTO_INCREMENT,ename VARCHAR(50),PRIMARY KEY(id));
-- 开启游标
OPEN my_cursor;
-- 循环开始
REPEAT
-- 将游标获取的值放局部变量里
FETCH my_cursor INTO tmp_value;
-- 更改flag值
SET flag=flag+1;
-- 插入语句
INSERT INTO test_cursor(ename) VALUES(tmp_value);
-- 游标到10时结束循环
UNTIL flag=10 END REPEAT;
-- 打印表数据
SELECT * FROM test_cursor;
-- 关闭游标
CLOSE my_cursor;
END

触发器

将事件与sql执行状况绑定,可使用NEW、OLD虚拟表访问具体操作的字段值

1
2
CREATE TRIGGER delete_question_msg AFTER DELETE ON question
FOR EACH ROW SELECT 'delete question finished!' INTO @msg;
1
2
DELETE FROM question WHERE qid=528;
SELECT @msg;

正则表达式基础符号概念

发表于 2016-05-17 | 分类于 java | | 阅读次数:

在项目中解析文件部分使用java Pattern与Matcher类,自然少不了正则啦,在此将正则基础符号概念部分总结一下,比较精简…


句点符号

. 任意单个字符

例:a.b 匹配三位长度字符串,中间任意字符;如a b,a1b,a-b等


方括号 圆括号

[] 单字符长度范围, () 分组(不限长度)

例:a[123]b 匹配三位长度字符串,中间指定字符;如a1b,a2b,a3b,()用法同理


或符号

| 或运算

例:(a|b|cc) 匹配a或b或cc,此处不可使用 []


星号

* 匹配左边字符0次或多次

例:a*[b] 匹配最后一位字符是b,前面字符全为a的字串或单个b字符,如ab,aaab,b


加号

+ 匹配左边字符1次或多次

例: a+[b] 匹配第二位是b字符,b字符前面一个或多个a字符的字符串,如aab,ab,aaab


问号

\? 匹配左边字符0次或一次(有或无此字符->有此字符也满足条件)

例:a?[ab] 匹配左边有a字符或无a字符,第二位为a或b的字符串,如a,ab,b


花括号

{n} | {m-n} 匹配左边字符恰好n次 或 m-n 次(连续性)

例:a{3}b只匹配字符串aaab


否符号

^ 非运算

例:[^a]b匹配第一个字符不是a,第二个字符是b的字符串


常用正则表达式符号

\s 等价于 [\t\n\r\f] 即tab制表符、换行符、回车符、换页符,其一,也就是空白字符

\d等价于 [0-9] 也就是数字字符

\w等价于 [A-Z0-9] 也就是字符,字母字符无论大小写都匹配哦

\S \D \W 即上面三种符号定义的“非”逻辑

解决url传中文参数乱码

发表于 2016-03-23 | 分类于 java | | 阅读次数:

解决url传中文参数乱码

首先在前台js内将相应的中文参数(qsInfo)编码两次,相应代码如下。可用encodeURIComponent直接对中文参数进行编码,直接使用encodeURIComponent对url进行编码会使 / 字符转为16进制字符,这时你会发现页面没有定向到url的地址…

1
2
3
4
var url=encodeURI(encodeURI('${ctx}/backstage/getPraxisQuestionList?qsInfo='+qsInfo));
or...
var url='${ctx}/backstage?qsInfo='+encodeURIComponent(encodeURIComponent(qsInfo));
//var url=encodeURIComponent(encodeURIComponent('${ctx}/backstage?qsInfo='+qsInfo));//url不跳转,/被编码了...


接着在java后台对相应字符进行解码就ok啦,代码如下

1
String qsInfo = URLDecoder.decode(question.getQsInfo(),"UTF-8");//字符解码,此处我的参数是直接封装在bean里的。

具体流程就是:前台编码2次,后台解码1次…

12
LiQiang

LiQiang

亦余心之所向兮,虽九死其犹未悔!

19 日志
6 分类
13 标签
GitHub E-Mail
© 2016 — 2019 LiQiang