游戏ID统一管理器DEMO

news2024/12/23 23:35:36

一般游戏的角色ID、名字,工会ID、名字,等最好统一创建,方便合服处理,可以以此基础,动态配置生成ID
这个也可以用openresty 作个,可能更专业点,

1:go1.20+ 最后一版支持win7的
mongodb v7.0.9
Redis v=7.2.4

2:代码
在这里插入图片描述
在这里插入图片描述
3:代码

package main

import (
	"fmt"
	"generateid/config"
	"generateid/httpserver"
	"generateid/mongodb"
	"generateid/redis"
	"github.com/rs/zerolog/log"
	"net"
	"os"
	"os/signal"
	"runtime"
	"syscall"
)

func main() {
	err := config.LoadConfig("config/generateuid.yaml")
	if err != nil {
		log.Error().Msgf("err: %v", err)
		os.Exit(0)
	}
	if err1 := mongodb.InitMongo(); err1 != nil {
		os.Exit(10)
	}
	if err2 := redis.InitRedis(); err2 != nil {
		os.Exit(10)
	}
	lis := httpserver.SetUpHttpServer(config.GetListenIpPort(), int32(config.GetListenMaxConn()), config.GetMd5Salt(), config.GetServerMOde())
	//exit
	sysType := runtime.GOOS
	if sysType == "windows" {
		winCheckExit(lis)
	} else if sysType == "linux" {
		linuxCheckExit(lis)
	}

}

func winCheckExit(lis net.Listener) {
	quitchan := make(chan struct{})
	go func() {
		<-quitchan
		fmt.Println("generateid server close")
		lis.Close()
	}()
	fmt.Println("generateid server setup")
	for {
		var strinput string
		fmt.Scan(&strinput)
		if strinput == "quit" {
			quitchan <- struct{}{}
		} else if strinput == "help" {
			fmt.Println("input quit to quit progress \n")
		}
	}
}

func linuxCheckExit(lis net.Listener) {
	quitchan := make(chan os.Signal)
	signal.Notify(quitchan,
		//	syscall.SIGINT, //ctrl+c
		syscall.SIGINT, //ctrl+/
		syscall.SIGTERM,
		syscall.SIGHUP, //终端结束
	)
	exitfun := func() {
		fmt.Println("generateid server close")
		lis.Close()
		//os.Exit(10)
	}
	for s := range quitchan {
		switch s {
		case syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT: //syscall.SIGHUP,
			fmt.Println("Program Exit...", s)
			exitfun()
		default:
			fmt.Println("other signal", s)
		}
	}
}

package httpserver

import (
	"context"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	gdef "generateid/gobaldefine"
	"generateid/mongodb"
	"go.mongodb.org/mongo-driver/bson"
	"log"
	"net/http"
	"reflect"
	"regexp"
	"strconv"
	"sync/atomic"
	"time"

	//	"sync"
	"encoding/json"
	"fmt"
	redis2 "generateid/redis"
	"golang.org/x/net/netutil"
	"net"
)

type HttpServer struct {
	//curnum  int32
	maxnum    int32
	debugmode bool
	md5salt   string

	maxcurrentuserid   uint32 //角色当前最大ID
	maxcurrentfamilyid uint32 //工会当前最大ID
}

func (self *HttpServer) GetValidUserId() uint32 {
	return atomic.AddUint32(&self.maxcurrentuserid, 1)
}

func (self *HttpServer) GetValidFamilyId() uint32 {
	return atomic.AddUint32(&self.maxcurrentfamilyid, 1)
}

func (self *HttpServer) SetUserId(uid uint32) {
	atomic.StoreUint32(&self.maxcurrentuserid, uid)
}
func (self *HttpServer) SetFamilyId(uid uint32) {
	atomic.StoreUint32(&self.maxcurrentfamilyid, uid)
}

var (
	httpser HttpServer
)

func GetHttpSerInstance() *HttpServer {
	return &httpser
}

const (
	SUCESS_GENERATE_NAME = 1  //User_UID 作为name
	ERROR_NONE           = 0  //没错误  成功的 OK的
	ERROR_NAME_INVALIDL  = -1 //名字有非法字符或重名或长度不对
	ERROR_NAME_DUPLICATE = -2 //重名
	ERROR_HANDSHAKE_KEY  = -3 //握手码错误
	ERROR_TIME_INVALIDL  = -4 // 超时

	ERROR_OTHER = -15 //其他错误
)

//return map[string]interface{}{
//"uname": name,
//"md5":   md5v,
//"sid":   sid,
//"st":    st,
//}

type JsonGenerateUidReq struct {
	UName string `json:"uname"`
	Md5v  string `json:"md5"`
	Sid   uint32 `json:"sid"`
	Stime int64  `json:"st"`
}

// 统一回复的
type JsonGenerateUidResult struct {
	Result int32  `json:"result"` // < 0  错误 -1 名字有非法字符或重名或长度不对  -2 握手码错误 -15 其他错误 >=0 OK 下面的有效
	Uid    uint32 `json:"uid"`
	Name   string `json:"name"`
}

//maxcurrentuserid  uint32		//角色当前最大ID
//maxcurrentfamilyid uint32//工会当前最大ID

// 得到client  ip
func RetmoteIp(r *http.Request) string {
	remoteaddr := r.RemoteAddr
	if ip := r.Header.Get("Remote_addr"); ip != "" {
		remoteaddr = ip
	} else {
		remoteaddr, _, _ = net.SplitHostPort(remoteaddr)
	}
	if remoteaddr == "::1" {
		remoteaddr = "127.0.0.1"
	}
	return remoteaddr
}

func getMd5Salt() string {
	return httpser.md5salt
}

func isDebugMode() bool {
	return httpser.debugmode
}

// 设置跨域
func setkuayu(pw http.ResponseWriter) {
	//设置跨域访问 nodejs
	/*app.all('*', function(req, res, next) {
		res.header("Access-Control-Allow-Origin", "*");
		res.header("Access-Control-Allow-Headers", "X-Requested-With");
		res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
		res.header("X-Powered-By",' 3.2.1')
		res.header("Content-Type", "application/json;charset=utf-8");
		next();
	});
	*/
	//(原因:CORS 头 'Access-Control-Allow-Origin' 不匹配 '*, Content-Type')
	pw.Header().Set("Access-Control-Allow-Origin", "*")
	pw.Header().Add("Access-Control-Allow-Headers", "Content-Type")
	pw.Header().Set("Content-Type", "application/json;charset=utf-8")
}

// 发送json 数据
func sendJson(w http.ResponseWriter, v interface{}) bool {

	if data, err := json.Marshal(v); err == nil {
		setkuayu(w)
		fmt.Fprint(w, string(data))
		log.Println(string(data))
		return true
	} else {
		log.Println(err)
		log.Println(v)
		return false
	}
}

func checkstring(str string) bool {
	//	str := "ABc123汉字./"
	//使用regexp.MustCompile函数编译正则表达式^[A-Za-z0-9\u4e00-\u9fa5./]+$,表示字符串只能包含大小写字母、数字、汉字、点和斜杠
	//exp := regexp.MustCompile(`^[A-Za-z0-9\u4e00-\u9fa5./]+$`)
	//exp := regexp.MustCompile(`^[A-Za-z0-9\u4e00-\u9fa5_-]+$`)
	//BASE64URL编码的流程:1、明文使用BASE64进行加密 2、在BASE64的基础上进行一下的编码:2.1)去除尾部的"=" 2.2)把"+"替换成"-" 2.3)把"/"替换成"_"
	//BASE64URL解码的流程:1)把"-"替换成"+". 2)把"_"替换成"/" . 3)(计算BASE64URL编码长度)%4 a)结果为0,不做处理 b)结果为2,字符串添加"==" c)结果为3,字符串添加"="
	exp := regexp.MustCompile(`^[A-Za-z0-9_-]+$`) //base64url
	if exp.MatchString(str) {
		//fmt.Println("字符串匹配成功")
		return true
	} else {
		//fmt.Println("字符串匹配失败")
		return false
	}
}

func GetStringValue(v reflect.Value) string {
	switch v.Kind() {
	case reflect.String:
		return v.String()
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return fmt.Sprintf("%v", v.Uint())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return fmt.Sprintf("%v", v.Int())
	}
	return ""
}

func geturlparams(r *http.Request) map[string]string {
	//Method specifies the HTTP method (GET, POST, PUT, etc.).
	params := map[string]string{}
	switch r.Method {
	case "GET":
		{
			query := r.URL.Query()
			for k, v := range query {
				params[k] = v[0]
			}
		}
	case "POST":
		{
			// 根据请求body创建一个json解析器实例
			decoder := json.NewDecoder(r.Body)
			// 用于存放参数key=value数据
			// 解析参数 存入map
			//t := make(map[string]interface{})
			//	decoder.Decode(&params)
			ts := &JsonGenerateUidReq{}
			if err := decoder.Decode(ts); err == nil {

				valueOf := reflect.ValueOf(ts) //.Type()
				if valueOf.Kind() == reflect.Pointer || valueOf.Kind() == reflect.Slice {
					valueOf = valueOf.Elem()
				}
				typeOf := reflect.TypeOf(ts)
				if typeOf.Kind() == reflect.Pointer || typeOf.Kind() == reflect.Slice {
					typeOf = typeOf.Elem()
				}

				for i := 0; i < valueOf.NumField(); i++ {
					fieldvalue := valueOf.Field(i)
					fieldtype := typeOf.Field(i)
					jsontag := fieldtype.Tag.Get("json")
					//	log.Printf("k:%v v=%v tag=%v kind=%v v=%v", fieldtype.Name, fieldvalue.Interface(), jsontag, fieldvalue.Kind(), GetStringValue(fieldvalue))

					params[jsontag] = GetStringValue(fieldvalue)
				}
			}

		}
	}
	//return params
	return params
}

//func closeconnect(w http.ResponseWriter) {
//	hj, ok := w.(http.Hijacker)
//	if !ok {
//		http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
//		return
//	}
//	conn, bufrw, err := hj.Hijack()
//	if err != nil {
//		http.Error(w, err.Error(), http.StatusInternalServerError)
//		return
//	}
//	// Don't forget to close the connection:
//	defer conn.Close()
//	conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
//	bufrw.WriteString("Now we're speaking raw TCP. Say hi: ")
//	bufrw.Flush()
//	s, err := bufrw.ReadString('\n')
//	if err != nil {
//		log.Printf("error reading string: %v", err)
//		return
//	}
//	fmt.Fprintf(bufrw, "You said: %q\nBye.\n", s)
//	bufrw.Flush()
//}

// ServeHTTP implements an http.Handler that answers RPC requests.
//func (server *server) servehttp(w http.responsewriter, req *http.request) {
//	if req.method != "connect" {
//		w.header().set("content-type", "text/plain; charset=utf-8")
//		w.writeheader(http.statusmethodnotallowed)
//		io.writestring(w, "405 must connect\n")
//		return
//	}
//	conn, _, err := w.(http.hijacker).hijack()
//	if err != nil {
//		log.print("rpc hijacking ", req.remoteaddr, ": ", err.error())
//		return
//	}
//	io.writestring(conn, "http/1.0 "+connected+"\n\n")
//	server.serveconn(conn)
//}

func errorrep(w http.ResponseWriter, errorcdoe int) {
	w.WriteHeader(errorcdoe)
	w.Write([]byte("error"))
}

func errormsgrep(w http.ResponseWriter, errorcode int32) {
	v := &JsonGenerateUidResult{}
	v.Result = errorcode
	sendJson(w, v)
}

func successrep(w http.ResponseWriter, res int32, uid uint32, name string) {
	v := &JsonGenerateUidResult{}
	v.Result = res
	v.Uid = uid
	v.Name = name
	sendJson(w, v)
}

func GenerateMd5Value(name string, sid uint32, st int64, salt string) string {
	plaintext := fmt.Sprintf("[%v#%v#%v#%v]", name, sid, st, salt)
	m5 := md5.New()
	m5.Write([]byte(plaintext))
	t := hex.EncodeToString(m5.Sum(nil))
	return t
	//by := m5.Sum([]byte(plaintext))
	//if len(by) == 64 {
	//	return fmt.Sprintf("%x", by[16:48]) //32byte
	//}
	//t := fmt.Sprintf("%x", by)
	//return t //string(m5.Sum([]byte(plaintext)))
	//32 get 16 [8:24]
	//64 get 32 [16:48]
}

func doGenerateName(tableindex uint32, w http.ResponseWriter, r *http.Request) {
	params := geturlparams(r)
	remoteip := RetmoteIp(r)
	if len(params) > 0 {
		vname, ok1 := params["uname"]
		//vhandshakekey, ok2 := params["md5"]
		vmd5, ok2 := params["md5"]
		vserverid, ok3 := params["sid"]
		vtime, ok4 := params["st"]
		if ok1 && ok2 && ok3 && ok4 {
			log.Printf("uname=%v md5=%v sid=%v st=%v", vname, vmd5, vserverid, vtime)
			sid, err1 := strconv.Atoi(vserverid)
			st, err2 := strconv.ParseInt(vtime, 10, 64)
			if err1 != nil || err2 != nil {
				log.Printf("ip=%v vserver=%v  name=%v", remoteip, vserverid, vname)
				errormsgrep(w, ERROR_NAME_INVALIDL)
				return
			}
			if vmd5 != GenerateMd5Value(vname, uint32(sid), st, getMd5Salt()) {
				if !isDebugMode() {
					//回复错误
					errormsgrep(w, ERROR_HANDSHAKE_KEY)
					return
				}
			}
			curtime := time.Now().Unix()
			if curtime > st+60 {
				if !isDebugMode() {
					//回复错误
					errormsgrep(w, ERROR_TIME_INVALIDL)
					return
				}
			}
			if len(vname) > 3 && len(vname) <= 64 && checkstring(vname) {
				//合法
				if uid, _ := GenerateName(tableindex, vname, false); uid > 0 {
					successrep(w, ERROR_NONE, uid, vname)
				} else {
					errormsgrep(w, ERROR_NAME_DUPLICATE)
				}

			} else if len(vname) <= 1 { //空
				//合法
				if uid, tanme := GenerateUidOnly(tableindex); uid > 0 {
					successrep(w, SUCESS_GENERATE_NAME, uid, tanme)
				} else {
					errormsgrep(w, ERROR_NAME_DUPLICATE)
				}
			} else {
				log.Printf("ip=%v vserver=%v  name=%v", remoteip, vserverid, vname)
				errormsgrep(w, ERROR_NAME_INVALIDL)
			}
			return
		}
	}
	errorrep(w, 404)
}

func handlerGenerateUserUid(w http.ResponseWriter, r *http.Request) {
	doGenerateName(gdef.TYPE_TABLE_USER, w, r)
}

func handlerGenerateFamilyUid(w http.ResponseWriter, r *http.Request) {
	doGenerateName(gdef.TYPE_TABLE_FAMILY, w, r)
}

//func clientfun(w http.ResponseWriter, r *http.Request) {
//
//	r.ParseForm()
//
//	path := r.URL.Path
//	remoteip := RetmoteIp(r)
//	log.Printf("path =%v remoteip=%v \n", path, remoteip)
//	//strings.Split(r.RemoteAddr,":")
//	switch path {
//	case "/get_serverinfo": //得到服务器版本信息
//		v := &jsondata.Serverinfo{}
//		v.Version = "2017081011"
//		v.State = int32(time.Now().Unix()) //正常
//		sendJson(w, v)
//	case "/login":
//		param_account, f1 := r.Form["account"]
//		param_token, f2 := r.Form["token"]
//		if f1 && f2 && len(param_account[0]) > 1 {
//			doLogin(w, param_account[0], param_token[0], remoteip)
//		}
//	}
//}

func SetUpHttpServer(httpaddr string, maxnum int32, md5salt string, bdebug bool) net.Listener {
	httpser.maxnum = maxnum
	httpser.debugmode = bdebug //true debug  false release  default false
	httpser.md5salt = md5salt  //握手key
	log.Printf("httpaddr:%v maxnum=%v \n", httpaddr, maxnum)

	l, err := net.Listen("tcp", httpaddr)
	if err != nil {
		log.Fatal("listen:", err)
	}
	lis := netutil.LimitListener(l, int(maxnum))
	http.HandleFunc("/uuid", handlerGenerateUserUid)
	http.HandleFunc("/fuid", handlerGenerateFamilyUid)
	
	//先清理下
	ClearCurIndexRedisDb()
	if !QueryMaxUserId() {
		log.Fatal("QueryMaxUserId:")
	}
	QueryAllUserName()
	if !QueryMaxFamilyId() {
		log.Fatal("QueryMaxFamilyId:")
	}
	QueryAllFamilyName()
	/
	go http.Serve(lis, nil)
	//go http.Serve(lis, http.HandlerFunc(clientfun))

	return lis
}

func QueryMaxUserId() bool {
	querytimeout := time.Second * 10 //30秒
	sort := bson.D{{"uid", -1}}
	uid, err := mongodb.GetMongoSyncInstance().FindOneMax(sort, gdef.TABLE_USER_UID, querytimeout)
	if err != nil {
		log.Printf("QueryMaxUserId error=%v", err.Error())
		return false
	}
	log.Printf("QueryMaxUserId(%v)", uid)
	if uid < gdef.BASE_USER_ID {
		uid = gdef.BASE_USER_ID
	}
	GetHttpSerInstance().SetUserId(uid)
	return true
}

func QueryMaxFamilyId() bool {
	querytimeout := time.Second * 10 //30秒
	sort := bson.D{{"uid", -1}}
	uid, err := mongodb.GetMongoSyncInstance().FindOneMax(sort, gdef.TABLE_Family_UID, querytimeout)
	if err != nil {
		log.Printf("QueryMaxFamilyId error=%v", err.Error())
		return false
	}
	log.Printf("QueryMaxFamilyId(%v)", uid)
	if uid < gdef.BASE_FAMILY_ID {
		uid = gdef.BASE_FAMILY_ID
	}
	GetHttpSerInstance().SetFamilyId(uid)
	return true
}

func QueryAllUserName() int64 {
	//如果想指定 <projection> 是包含字段,那所有字段值都得统一是 1,相反如果是不包含,也必须都是 0
	//projection 里要不写1 要么写0 不能混合在里面
	projection := bson.D{
		{"uid", 0},
		//	{"name", 1},
		{"_id", 0},
	}
	//
	mongodb_table := gdef.TABLE_USER_UID
	redis_table := gdef.REDIS_TABLE_USER
	querytimeout := time.Second * 30 //30秒
	cursor, err := mongodb.GetMongoSyncInstance().FindAll(projection, mongodb_table, querytimeout)
	if err != nil {
		return -1
	}
	user := new(gdef.UserInfo)
	rs := redis2.GetRedisSyncClient()
	for cursor.Next(context.Background()) {
		if err = cursor.Decode(user); err != nil {
			return -2
		}
		rs.SAdd_ReturnVoid(redis_table, user.Name)
	}
	totalnum := rs.SCard(redis_table)
	log.Printf("QueryName %v(%v)", mongodb_table, totalnum)

	return totalnum
}

func QueryAllFamilyName() int64 {
	//如果想指定 <projection> 是包含字段,那所有字段值都得统一是 1,相反如果是不包含,也必须都是 0
	//projection 里要不写1 要么写0 不能混合在里面
	projection := bson.D{
		{"uid", 0},
		//	{"name", 1},
		{"_id", 0},
	}
	//
	mongodb_table := gdef.TABLE_Family_UID
	redis_table := gdef.REDIS_TABLE_FAMILY
	querytimeout := time.Second * 30 //30秒
	cursor, err := mongodb.GetMongoSyncInstance().FindAll(projection, mongodb_table, querytimeout)
	if err != nil {
		return -1
	}
	user := new(gdef.FamilyInfo)
	rs := redis2.GetRedisSyncClient()
	for cursor.Next(context.Background()) {
		if err = cursor.Decode(user); err != nil {
			return -2
		}
		rs.SAdd_ReturnVoid(redis_table, user.Name)
	}
	totalnum := rs.SCard(redis_table)
	log.Printf("QueryName %v(%v)", mongodb_table, totalnum)
	return totalnum
}

func CheckNameVaild(name string) bool {
	namelen := len(name)
	if namelen < 3 {
		return false
	}
	//user_  family_
	if (namelen > 5 && name[:5] == "user_") || (namelen > 7 && name[:7] == "family_") {
		return false
	}
	return true
}

func GenerateName(tableindex uint32, name string, bGenerateName bool) (uint32, string) {
	mongodb_table := ""
	redis_table := ""
	//	fGetUid := func() uint32 { return 0 }
	switch tableindex {
	case gdef.TYPE_TABLE_USER:
		mongodb_table = gdef.TABLE_USER_UID
		redis_table = gdef.REDIS_TABLE_USER
	//	fGetUid = GetHttpSerInstance().GetValidUserId
	//	data = &gdef.UserInfo{}
	case gdef.TYPE_TABLE_FAMILY:
		mongodb_table = gdef.TABLE_Family_UID
		redis_table = gdef.REDIS_TABLE_FAMILY
	//	fGetUid = GetHttpSerInstance().GetValidFamilyId
	//	data = &gdef.FamilyInfo{}
	default:
		return 0, ""
	}
	rs := redis2.GetRedisSyncClient()
	mgdb := mongodb.GetMongoSyncInstance()
	if CheckNameVaild(name) {
		nRet := rs.SAdd(redis_table, name)
		switch {
		case nRet < 0:
			return 0, "Error"
		case nRet == 0: //重名了
			return 0, "DuplicateName"
		default: //1 ok
			break
		}
	} else {
		return 0, "Name len error"
	}

	inserttimeout := time.Second * 3 //3秒
	plaintext_name := name           //明文name
	switch tableindex {
	case gdef.TYPE_TABLE_USER:
		uid := GetHttpSerInstance().GetValidUserId()
		//if bGenerateName && len(name) < 3 {
		//	plaintext_name = " user_" + strconv.Itoa(int(uid))
		//	name = base64.URLEncoding.EncodeToString([]byte(plaintext_name))
		//}
		data := &gdef.UserInfo{uid, plaintext_name}
		if _, err := mgdb.InsertData(data, mongodb_table, inserttimeout); err == nil {
			//	rs.SAdd(redis_table, name)
			return uid, name
		}
		rs.SRem(redis_table, name)
	case gdef.TYPE_TABLE_FAMILY:
		uid := GetHttpSerInstance().GetValidFamilyId()
		//if bGenerateName && len(name) < 3 {
		//	plaintext_name = "family_" + strconv.Itoa(int(uid))
		//	name = base64.URLEncoding.EncodeToString([]byte(plaintext_name))
		//}

		data := &gdef.FamilyInfo{uid, plaintext_name}
		if _, err := mgdb.InsertData(data, mongodb_table, inserttimeout); err == nil {
			//rs.SAdd(redis_table, name)
			return uid, name
		} else {
			rs.SRem(redis_table, name)
		}
	}
	return 0, "Errorother"
}

func GenerateUidOnly(tableindex uint32) (uint32, string) {
	mongodb_table := ""
	//	redis_table := ""
	name := ""
	//	fGetUid := func() uint32 { return 0 }
	switch tableindex {
	case gdef.TYPE_TABLE_USER:
		mongodb_table = gdef.TABLE_USER_UID
	//	redis_table = gdef.REDIS_TABLE_USER
	//	fGetUid = GetHttpSerInstance().GetValidUserId
	//	data = &gdef.UserInfo{}
	case gdef.TYPE_TABLE_FAMILY:
		mongodb_table = gdef.TABLE_Family_UID
	//	redis_table = gdef.REDIS_TABLE_FAMILY
	//	fGetUid = GetHttpSerInstance().GetValidFamilyId
	//	data = &gdef.FamilyInfo{}
	default:
		return 0, ""
	}
	//	rs := redis2.GetRedisSyncClient()
	mgdb := mongodb.GetMongoSyncInstance()
	inserttimeout := time.Second * 3 //3秒
	plaintext_name := ""             //明文name
	switch tableindex {
	case gdef.TYPE_TABLE_USER:
		uid := GetHttpSerInstance().GetValidUserId()
		plaintext_name = " user_" + strconv.Itoa(int(uid))
		name = base64.URLEncoding.EncodeToString([]byte(plaintext_name))
		log.Printf("temp return uid(%v) name(%v)", uid, name)
		//return uid, name
		//下面的插入有问题,有时间再修正下
		data := &gdef.UserInfo{uid, plaintext_name}
		if _, err := mgdb.InsertData(data, mongodb_table, inserttimeout); err == nil {
			//rs.SAdd(redis_table, name)
			return uid, name
		} else {
			log.Printf("mgdb.InsertData error=%v", err.Error())
		}
	case gdef.TYPE_TABLE_FAMILY:
		uid := GetHttpSerInstance().GetValidFamilyId()
		plaintext_name = "family_" + strconv.Itoa(int(uid))
		name = base64.URLEncoding.EncodeToString([]byte(plaintext_name))

		data := &gdef.FamilyInfo{uid, plaintext_name}
		if _, err := mgdb.InsertData(data, mongodb_table, inserttimeout); err == nil {
			return uid, name
		}
	}
	return 0, "Errorother"
}

// 清理当前索引的redis DB
func ClearCurIndexRedisDb() {
	redis2.GetRedisSyncClient().FlushDB()
}

/*
//for _, v := range username {
		//	rediscli.SAdd(tableuser, v)
		//}
		//realnum := rediscli.SCard(tableuser).Val()
		//if int64(namecount) != realnum {
		//	log.Fatal("queryallrolename num is equal org=%v read=%v ", namecount, realnum)
		//}
		//doc := cursor.Current
		//doc.Index(0)

		//assert.False(t, containsKey(doc, "_id"))
		//assert.True(t, containsKey(doc, "item"))
		//assert.True(t, containsKey(doc, "status"))
		//assert.False(t, containsKey(doc, "size"))
		//assert.False(t, containsKey(doc, "instock"))
		//for cursor.Next(context.TODO()) {
		//	// A new result variable should be declared for each document.
		//	var result bson.M
		//	if err := cursor.Decode(&result); err != nil {
		//		log.Fatal(err)
		//	}
		//	fmt.Println(result)
		//}
*/

4:测试,这个没什么好测试的,
有空可以找找原来用openresty+mysql做的,
这里的demo工程有需要再上传

如果觉得有用,麻烦点个赞,加个收藏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1987303.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序乡村医疗系统,源码、部署+讲解

目录 摘 要 Abstract 1 绪论 1.1 研究背景及意义 1.2 研究现状 1.3 研究内容 2 相关技术介绍 2.1 Java 语言 2.2 MySQL 数据库 2.3 Spring Boot 框架 2.4 B/S 结构 2.5 微信小程序 3 系统分析 3.1 可行性分析 3.1.1 经济可行性 3.1.2 技术可行性…

4.MySQL数据类型

目录 数据类型 ​编辑数值类型 tinyint类型 bit类型 float类型 decimal类型 字符串类型 char类型 varchar varchar和char的区别 日期和时间类型 数据类型 数值类型 说明一下&#xff1a;MySQL本身是不支持bool类型的&#xff0c;当把一个数据设置成bool类型时&#x…

【ThreadLocal总结】

文章目录 为什么使用ThreadLocalThreadLocal核心ThreadLocal内部结构ThreadLocal内存泄漏解决内存泄漏 为什么使用ThreadLocal 在并发编程中&#xff0c;多个线程同时访问和修改共享变量是一个常见的场景。这种情况下&#xff0c;可能会出现线程安全问题&#xff0c;即多个线程…

AWS生成式AI项目的全生命周期管理

随着人工智能技术的迅速发展&#xff0c;生成式 AI 已成为当今最具创新性和影响力的领域之一。生成式 AI 能够创建新的内容&#xff0c;如文本、图像、音频等&#xff0c;具有广泛的应用前景&#xff0c;如自然语言处理、计算机视觉、创意设计等。然而&#xff0c;构建一个成功…

【Python】pandas:计算,统计,比较

pandas是Python的扩展库&#xff08;第三方库&#xff09;&#xff0c;为Python编程语言提供 高性能、易于使用的数据结构和数据分析工具。 pandas官方文档&#xff1a;User Guide — pandas 2.2.2 documentation 帮助&#xff1a;可使用help(...)查看函数说明文档&#xff0…

文本编辑器小型架构

C字体库开发之字体列表设计七-CSDN博客 创作不易&#xff0c;小小的支持一下吧&#xff01;

odoo from样式更新

.xodoo_form {.o_form_sheet {padding-bottom: 0 !important;border-style: solid !important;border-color: white;}.o_inner_group {/* 线框的样式 *//*--line-box-border: 1px solid #666;*//*box-shadow: 0 1px 0 #e6e6e6;*/margin: 0;}.grid {display: grid;gap: 0;}.row …

【数据结构】排序 —— 归并排序(mergeSort)、计数排序、基数排序

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构、LeetCode专栏 &#x1f4da;本系…

【数据结构】哈希应用-STL-位图

目录 1、位图的概念 2、位图的设计与实现 2.1 set 2.2 reset 2.3 test 3、C库中的位图 4、位图的优缺点 5、位图相关题目 1、位图的概念 面试题&#xff1a;给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这4…

【Material-UI】按钮组件中的实验性API:Loading按钮详解

文章目录 一、LoadingButton 组件概述1. 组件介绍2. 基本用法 二、LoadingButton 组件的高级用法1. 自定义加载指示器2. 图标与加载位置 三、已知问题与解决方法1. Chrome 翻译工具与 LoadingButton 的兼容性问题 四、实用性与未来展望1. 应用场景2. 未来展望 五、总结 Materia…

共享内存的原理及初识线程

char *str"hello world"; *str-H; 运行时报错&#xff0c;RWX只有R权限。 外设和内存交互以4KB为单位。 虚拟地址32位的划分为10 10 12 前10位对应页表的页目录。 在10位即为页表&#xff0c;页表中存放指定页框的起始物理地址虚拟地址的低12位作为页内偏移。 共…

RedLock算法分析

Redis分布式锁-RedLock算法 手写分布式锁的缺点 Redlock算法设计理念 Redis也提供了Redlock算法&#xff0c;用来实现基于多个实例的分布式锁。 锁变量由多个实例维护&#xff0c;即使有实例发生了故障&#xff0c;锁变量仍然是存在的&#xff0c;客户端还是可以完成锁操作。…

第一篇Linux介绍

目录 1、操作系统 2、Windows和Linux操作系统的区别 3、 Linux 的发行版本 4、 linux 分支 5、 Linux 的含义 6、Linux 特点 1、操作系统 常见操作系统有&#xff1a;Windows、MacOS、Unix/Linux。 类 UNIX Windows&#xff1a;其是微软公司研发的收费操作系统&#xff…

【漏洞复现】JBoss 中间件漏洞

JBoss介绍 JBoss是⼀个基于J2EE的开发源代码的应⽤服务器。JBoss代码遵循LGPL许可&#xff0c;可以在任何商业应⽤中免费使⽤。JBoss是⼀个管理EJB的容器和服务器&#xff0c;⽀持EJB1.1、EJB 2.0和EJB3的规范。但JBoss核⼼服务不包括⽀持servlet/JSP的WEB容器&#xff0c;⼀般…

QTableView使用示例-Qt模型视图委托(MVD)(Model-View-Delegate)

模型视图委托&#xff08;MVD&#xff09;是Qt中特有的设计模式&#xff0c;类似MVC设计模式&#xff0c;将MVC设计模式中的Controller当做MVD中的Delegate&#xff0c;两者的概念基本相同。不同的是委托不是独立存在&#xff0c;而是包含在视图里面。 模型视图委托设计模式中&…

步进电机驱动调试问题

工作中&#xff0c;调试24-byj48步进电机遇到一个怪现象&#xff1a; 1. 偶现 2. 出现问题时其中一个马达反转无法驱动&#xff0c;正转正常。 排查思路&#xff1a; 1. 将两个电机交叉验证&#xff0c;发现始终跟M2接口有关。排除电机问题。 2. 检查电机IO口配置&#xf…

大数据项目——广告数仓之HTTP概述

目录 第一章、理解URL 1.1 客户端、服务器 1.1.1 服务器与服务 1.1.2 客户端 1.2 URL 1.3 查询参数 第一章、理解URL 1.1 客户端、服务器 1.1.1 服务器与服务 所谓服务器&#xff0c;其实就是一台24小时不关机的计算机&#xff0c;它也有自己的cpu、内存、网卡、…

Docker更新镜像源小记

Docker镜像源无法访问 进入docker目录 cd /etc/docker/编辑daemon.json文件&#xff0c;如果没有&#xff0c;则新建 {"registry-mirrors": ["https://dockerproxy.cn"] }收集一些镜像源地址&#xff0c;未测是否能用 “https://hub.uuuadc.top”,“htt…

Android 埋点信息分析——内存篇

源码基于&#xff1a;Android U 0. 前言 在前一篇《Android statsd 埋点简析》一文中简单剖析了Android 埋点采集、传输的框架&#xff0c;本文在其基础对埋点信息进行解析&#xff0c;来看下Android 中埋下的内存信息有哪些。 1. 通过代码剖析google 埋点内容 1.1 PROCESS_M…

网络安全之sql靶场(11-23)

sql靶场&#xff08;11-23&#xff09; 目录 第十一关&#xff08;post注入&#xff09; 第十二关 第十三关 第十四关 第十五关 第十六关 第十七关 第十八关 第十九关 第二十关 第二十一关 第二十二关 第二十三关 第十一关&#xff08;post注入&#xff09; 查看…