目录
Go
C++
Go
1.转移函数StructTo()
- 用于结构体、map、json格式的字符串之间的数据转移
- 依据是tag的json标签
- 注意,使用时要StructTo(&xx1, &xx2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16tp := reflect.TypeOf(old)
var bt []byte
var err error
if tp.Name() == "string" {
bt = []byte(old.(string))
} else {
bt, err = json.Marshal(old)
if err != nil {
return err
}
}
err = json.Unmarshal(bt, new)
if err != nil {
return err
}
return nil
2.反转数组
1 | func ReverseSlice(s interface{}) { |
3.正则判断字符串格式
- 判断手机号
1
2
3
4
5
6func VerifyMobileFormat(mobileNum string) bool {
regular := "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$"
reg := regexp.MustCompile(regular)
return reg.MatchString(mobileNum)
} - 判断身份证号
1
2
3
4
5
6
7
8func VerifyIdFormat(id string) bool {
if len(id) < 18 {
return false
}
pattern := `^([1-6][1-9]|50)\d{4}(18|19|20)\d{2}((0[1-9])|10|11|12)(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`
reg := regexp.MustCompile(pattern)
return reg.MatchString(id)
} - 判断邮箱
1
2
3
4
5
6func VerifyEmailFormat(email string) bool {
pattern := `^[0-9a-z][_.0-9a-z-]{0,31}@([0-9a-z][0-9a-z-]{0,30}[0-9a-z]\.){1,4}[a-z]{2,4}$`
reg := regexp.MustCompile(pattern)
return reg.MatchString(email)
}
4.md5加密明文
- 一般使用这样的形式保存用户密码Md5Make(password + salt + ApiSecret)
- salt是用户表里每个用户独有随机生成的字段
- ApiSecret是config配置文件里配置的字符串
- 好处是攻击者获得数据库或获得config文件之一都不能破解密码,只有二者都具备才能解密
1
2
3
4
5func Md5Make(s string) string {
md5 := md52.New()
md5.Write([]byte(s))
return hex.EncodeToString(md5.Sum(nil))
}
5.项目根据配置文件初始化
- 初始化配置文件config.json,以有user和admin两个端的项目为例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24{
"userWeb" : {
"api_port" : "8085",
"api_secret": "RaFmEoUgGINWwHecXGEhfKogPfRyzgfL",
"save_dir" : "/api/upload/user_web/"
},
"adminWeb" : {
"api_port" : "8086",
"api_secret" : "gLhUdClFoIfppzAUzYZigbaEEdaOwYQc",
"save_dir" : "/api/upload/admin_web/"
},
"mysql" : {
"driver": "mysql",
"user": "root",
"pass_word": "123456",
"host": "121.5.184.118",
"port": "3306",
"db_name": "xy",
"charset": "utf8mb4",
"show_sql": true, //用于给engine.ShowSQL(mysql.ShowSql)调用,表示是否往控制台打印sql语句
"parseTime": "true",
"loc": "Asia/Shanghai"
}
} - 初始化方法ConfigInit()
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
57type Config struct {
Mysql Mysql
UserWeb UserWeb
AdminWeb AdminWeb
}
type Mysql struct {
Driver string `json:"driver"`
User string `json:"user"`
PassWord string `json:"pass_word"`
Host string `json:"host"`
Port string `json:"port"`
DbName string `json:"db_name"`
Charset string `json:"charset"`
ShowSql bool `json:"show_sql"`
ParseTime string `json:"parse_time"`
Loc string `json:"loc"`
}
type UserWeb struct {
ApiPort string `json:"api_port"`
ApiSecret string `json:"api_secret"`
SaveDir string `json:"save_dir"`
}
type AdminWeb struct {
ApiPort string `json:"api_port"`
ApiSecret string `json:"api_secret"`
SaveDir string `json:"save_dir"`
}
var Conf *Config
//path存的是config.json的路径,一般用os.GetWd()+"xx"获取
func ConfigInit(path string) *Config {
config := new(Config)
file, err := os.Open(path)
if err != nil {
fmt.Println(err)
panic("打开配置文件错误" + path)
}
confByte, err := ioutil.ReadAll(file)
if err != nil {
panic("读取配置文件错误")
}
err = json.Unmarshal(confByte, config)
if err != nil {
fmt.Println(err)
fmt.Println("*************")
fmt.Println(path)
panic("解析配置文件错误")
}
Conf = config
return Conf
}
6.项目的main()函数
1 | func main() { |
7.路由文件router.go的写法
1 | func RouterInit() *gin.Engine { |
8.jwt生成和解密token
- 使用传入的user_ id、branch_id,再加上配置文件里的secret字段生成token
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19func GetToken(userId, branchId int,secret string) (string,error) {
appSecret := secret
jwtKey = []byte(appSecret)
claim := &MyClaims{
UserId: userId,
BranchId: branchId,
StandardClaims:jwt.StandardClaims{
IssuedAt: time.Now().Unix(),
Subject: "userToken",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)
tokenString, err := token.SignedString(jwtKey)
if err != nil {
return "", err
}
return tokenString, nil
} - 解析token
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15func ParseToken(tokenString string)(*MyClaims, error) {
claim := &MyClaims{}
jwtKey = []byte(config.Conf.AdminWeb.ApiSecret)
token,err := jwt.ParseWithClaims(tokenString,claim, func(token *jwt.Token) (interface{}, error) {
return jwtKey,nil
})
if err == nil && token != nil {
if token.Valid {
return claim,nil
} else {
return nil,errors.New("token解析失败")
}
}
return nil,err
} - 使用
- 生成token
1
token, err := jwt.GetToken(admin.Id, admin.BranchId, config.Conf.AdminWeb.ApiSecret)
- 中间件内解析token,并将其绑定到ctx上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//基础写法
myClaims, err := jwt.ParseToken(token)
ctx.Set("user_id", myClaims.UserId)
ctx.Set("branch_id", myClaims.BranchId)
//另一种保证安全性的写法,即保证token解析出来的token要在数据库里存在,如果上一步由于某种原因生成了错误的token,这里就会判断不存在并返回解析失败
myClaims, err := jwt.ParseToken(token)
user := &models.XyAdministrator{
Id: myClaims.UserId,
BranchId: myClaims.BranchId,
}
user, flag, err := store.Client().Admin().Get(ctx, user)
if !flag {
code.BuildReturn(ctx, 0, "解析失败", "")
ctx.Abort()
return
}
ctx.Set("user_id", user.Id)
ctx.Set("branch_id", user.BranchId)
- 生成token
9.上传文件接口的实现
- 接受前端传的文件,得到一个*multipart.FileHeader类型变量
1
2
3type param struct {
File *multipart.FileHeader `form:"file" json:"file" binding:"required"`
} - 用一个util包里的函数处理*multipart.FileHeader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22func Upload(host, dir string, file *multipart.FileHeader) (string, error) {
//获取当前运行路径
now, err := os.Getwd()
//创建文件夹
dir = now + dir
err = os.MkdirAll(dir, os.ModePerm)
//创建空白文件
fileName := dir + file.Filename
f, err := os.Create(fileName)
//打开*multipart.FileHeader文件
defer f.Close()
open, err := file.Open()
//将打开的文件中的数据复制到刚刚创建的空白文件中
_, err = io.Copy(f, open)
url := host + fileName
return url, nil
} - 调用方式
1
2
3host := ctx.Request.Host //服务器地址
dir := config.Conf.UserWeb.SaveDir //文件存储路径
url, err := util.Upload(host, dir, request.File)
10.支付的实现
- service层
- 新建一个rpc对象,并用以下形式发起一个rpc请求
1
2
3
4
5
6
7
8
9
10req := &protos.PayRequest{
Corp: int32(corpId), //config.json文件里写好的值
Amount: amount,
Order: order.OrderNo,
Open: user.OpenId,
Notify: "https://sbd.sc-edu.com/qyk_app/qyk_app_api/order/callback", //支付完成后微信用Post访问的回调路由
//Notify: "www.baidu.com",
Body: "华澳教育课程购买",
}
pay, err := rpcS.Pay.GetPay(ctx, req)
- 新建一个rpc对象,并用以下形式发起一个rpc请求
- 回调路由的controller层
- 处理回调内容,并回调rpc
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
40func (c *ControllerOrder) OrderCallBack(ctx *gin.Context) {
// 回复微信的数据
rsp := new(wechat.NotifyResponse)
//获取参数
notifyReq, err := wechat.ParseNotifyToBodyMap(ctx.Request)
fmt.Println(notifyReq)
payTime := notifyReq["time_end"].(string)
if err != nil {
rsp.ReturnCode = gopay.FAIL
ctx.String(http.StatusOK, "%s", rsp.ToXmlString())
return
}
//校验签名
ok, err := wechat.VerifySign("fia6iaFI2iUhvzsHk4rfV9w4PRZLfZVk", wechat.SignType_MD5, notifyReq)
if !ok {
rsp.ReturnCode = gopay.FAIL
ctx.String(http.StatusOK, "%s", rsp.ToXmlString())
return
}
order, _ := c.store.Order().GetByOrderNo(ctx, notifyReq["out_trade_no"].(string))
if notifyReq["result_code"].(string) == "SUCCESS" && notifyReq["return_code"].(string) == "SUCCESS" {
//rpc回调
rpcS, _ := rpc.NewRpcService()
req := &protos.NotifyRequest{
Order: order.OrderNo,
PayTime: times,
}
_, err = rpcS.Pay.PayNotify(ctx, req)
if err != nil {
fmt.Println(err.Error())
}
rsp.ReturnCode = gopay.SUCCESS
ctx.String(http.StatusOK, "%s", rsp.ToXmlString())
return
}
rsp.ReturnCode = gopay.FAIL
ctx.String(http.StatusOK, "%s", rsp.ToXmlString())
return
} - 处理数据库相关字段的更新
- 处理回调内容,并回调rpc
11.处理过期15分钟的未支付订单脚本
1 | package main |
C++
1.字符串分割
- 以char分割版本
1
2
3
4
5
6
7
8
9
10
11vector<string> split(string& str, char target){
vector<string> res;
string temple = "";
istringstream templeStream(str);
while (getline(templeStream, temple, target)){
res.push_back(temple);
}
return res;
} - 以string分割版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23vector<string> splitWithStl(const string &str,const string &pattern){
vector<string> resVec;
if ("" == str)
{
return resVec;
}
//方便截取最后一段数据
string strs = str + pattern;
size_t pos = strs.find(pattern);
size_t size = strs.size();
while (pos != string::npos)
{
string x = strs.substr(0,pos);
resVec.push_back(x);
strs = strs.substr(pos+1,size);
pos = strs.find(pattern);
}
return resVec;
}