【工具】常用工具代码

目录

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
    16
    tp := 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
2
3
4
5
6
7
func ReverseSlice(s interface{}) {
size := reflect.ValueOf(s).Len()
swap := reflect.Swapper(s)
for i, j := 0, size-1; i < j; i, j = i+1, j-1 {
swap(i, j)
}
}

3.正则判断字符串格式

  • 判断手机号
    1
    2
    3
    4
    5
    6
    func 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
    8
    func 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
    6
    func 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
      5
      func 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
    57
    type 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
2
3
4
5
6
7
8
9
10
11
func main() {
dir, err := os.Getwd()
if err != nil {
panic(err)
}
config.ConfigInit(dir + "/internal/configs/config.json") //根据配置文件初始化

router := user_web_api.RouterInit() //采用user_web_api文件夹里面的router.go

_ = router.Run(":" + config.Conf.UserWeb.ApiPort) //采用配置文件里的端口号
}

7.路由文件router.go的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func RouterInit() *gin.Engine {
//gin常规写法,过两个默认中间件
router := gin.Default()
router.Use(gin.Logger())
router.Use(gin.Recovery())

router.Use(auth.CORS()) //自己写的解决跨域问题的
mysqlStore, err := mysql.GetMysqlFactory() //使用工厂生成mysqlStore对象
store.SetClient(mysqlStore)
pkg.SetStore(mysqlStore)

app := router.Group("/xy_web_user") //设置总路由
userRouter := app.Group("/user") //设置子路由分组
{
userController := user.NewUserController(mysqlStore)
userRouter.POST("/login", userController.UserLogin)
userRouter.Use(auth.MiddleWare()) //在此之下的路由都会过中间件
userRouter.POST("/update_password", userController.UpdatePassword)
}
return router
}

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
    19
    func 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
    15
    func 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)

9.上传文件接口的实现

  • 接受前端传的文件,得到一个*multipart.FileHeader类型变量
    1
    2
    3
    type 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
    22
    func 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
    3
    host := 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
      10
      req := &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)
  • 回调路由的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
      40
      func (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
      }
    • 处理数据库相关字段的更新

11.处理过期15分钟的未支付订单脚本

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
package main

import (
"430api/internal/qyk_back_api/store/mysql"
"fmt"
"time"
)

func main() {
fmt.Println("SyncWxCourseOrder" + time.Now().Format("2006-01-02 15:04:05"))
d, _ := time.ParseDuration("-15m")
pastTime := time.Now().Add(d).Format("2006-01-02 15:04:05")

mysqlStore, _ := mysql.GetMysqlFactory()
orderList, err := mysqlStore.Order().GetListForPastTime(pastTime)
if err != nil {
fmt.Println(err)
return
}
for _, qykOrder := range orderList {
_ = mysqlStore.OrderDetail().UpdateForPastTime(qykOrder.Id)
}
fmt.Println("over")
}

C++

1.字符串分割

  • 以char分割版本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    vector<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
    23
    vector<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;
    }