Browse Source

update model

朱金辉 2 years ago
parent
commit
f9d6b734a4

+ 35 - 0
config/config.local.yaml

@@ -2,6 +2,17 @@ mode: "dev"
 #mode: "release"
 port: 8084
 cache_dir: "cache"
+upload:
+  dir: "upload"
+  fonts: "upload/fonts"
+  material: "upload/material"
+  max_file_size: 104857600
+  max_size: "1024*768"
+jwt:
+  prefix: "poster_"
+  secret: "aLKsO34kD"
+  project: "poster"
+  expire_at: 86400
 encrypt:
   key: "OdeOe93Dxf"
   expire_at: 86400
@@ -17,6 +28,30 @@ mysql:
   dbname: "poster"
   max_open_conns: 2000
   max_idle_conns: 50
+replicas[user]:
+  host: 127.0.0.1
+  port: 3306
+  user: "root"
+  password: "A119328118a"
+  dbname: "poster"
+  max_open_conns: 2000
+  max_idle_conns: 50
+replicas[order]:
+  host: 127.0.0.1
+  port: 3306
+  user: "root"
+  password: "A119328118a"
+  dbname: "poster"
+  max_open_conns: 2000
+  max_idle_conns: 50
+replicas[other]:
+  host: 127.0.0.1
+  port: 3306
+  user: "root"
+  password: "A119328118a"
+  dbname: "poster"
+  max_open_conns: 2000
+  max_idle_conns: 50
 redis:
   host: 127.0.0.1
   port: 6379

+ 30 - 8
config/settings.go

@@ -9,20 +9,39 @@ import (
 var Conf = new(AppConfig)
 
 type AppConfig struct {
-	Mode         string `mapstructure:"mode"`
-	Port         int    `mapstructure:"port"`
-	CacheDir     string `mapstructure:"cache_dir"`
-	*Encrypt     `mapstructure:"encrypt"`
-	*LogConfig   `mapstructure:"log"`
-	*MySQLConfig `mapstructure:"mysql"`
-	*RedisConfig `mapstructure:"redis"`
+	Mode             string        `mapstructure:"mode"`
+	Port             int           `mapstructure:"port"`
+	CacheDir         string        `mapstructure:"cache_dir"`
+	Upload           *UploadConfig `mapstructure:"upload"`
+	ReplicasForUser  *MySQLConfig  `mapstructure:"replicas[user]""`
+	ReplicasForOrder *MySQLConfig  `mapstructure:"replicas[order]""`
+	ReplicasForOther *MySQLConfig  `mapstructure:"replicas[other]""`
+	*EncryptConfig   `mapstructure:"encrypt"`
+	*JWTConfig       `mapstructure:"jwt"`
+	*LogConfig       `mapstructure:"log"`
+	*MySQLConfig     `mapstructure:"mysql"`
+	*RedisConfig     `mapstructure:"redis"`
 }
 
-type Encrypt struct {
+type EncryptConfig struct {
 	Key      string `mapstructure:"key"`
 	ExpireAt int64  `mapstructure:"expire_at"`
 }
 
+type UploadConfig struct {
+	Dir         string `mapstructure:"dir"`
+	Fonts       string `mapstructure:"fonts"`
+	Material    string `mapstructure:"material"`
+	MaxFileSize int64  `mapstructure:"max_file_size"`
+	MaxSize     string `mapstructure:"max_size"`
+}
+
+type JWTConfig struct {
+	Prefix   string `mapstructure:"prefix"`
+	Secret   string `mapstructure:"secret"`
+	Project  string `mapstructure:"project"`
+	ExpireAt int64  `mapstructure:"expire_at"`
+}
 type LogConfig struct {
 	Level       string `mapstructure:"level"`
 	WebLogName  string `mapstructure:"web_log_name"`
@@ -52,6 +71,7 @@ var (
 	devFilePath     = "./config/config.dev.yaml"
 	releaseFilePath = "./config/config.online.yaml"
 	localFilePath   = "./config/config.local.yaml"
+	testFilePath    = "../config/config.local.yaml"
 )
 
 func Init(mode string) {
@@ -60,6 +80,8 @@ func Init(mode string) {
 		filePath = devFilePath
 	} else if mode == "release" {
 		filePath = releaseFilePath
+	} else if mode == "test" { //for test only
+		filePath = testFilePath
 	} else { // local
 		filePath = localFilePath
 	}

+ 5 - 2
controller/index.go

@@ -1,9 +1,12 @@
 package controller
 
-import "github.com/gin-gonic/gin"
+import (
+	"github.com/gin-gonic/gin"
+	"icloudapp.cn/tools/entity"
+)
 
 func Index(c *gin.Context) {
 	s := make(map[string]interface{})
 	s["msg"] = "success"
-	ResponseSuccess(c, s)
+	entity.ResponseSuccess(c, entity.EmptyBodyObject())
 }

+ 57 - 34
controller/poster.go

@@ -7,14 +7,17 @@ import (
 	setting "icloudapp.cn/tools/config"
 	"icloudapp.cn/tools/entity"
 	"icloudapp.cn/tools/model"
+	"icloudapp.cn/tools/util"
 	"icloudapp.cn/tools/util/image"
+	utString "icloudapp.cn/tools/util/string"
 	"os"
+	"time"
 )
 
 func Preview(ctx *gin.Context) {
 	file := ctx.Query("file")
 	if file == "" {
-		ResponseError(ctx, entity.CodePageNotFound)
+		entity.ResponseError(ctx, entity.CodePageNotFound)
 		return
 	}
 
@@ -26,14 +29,14 @@ func Preview(ctx *gin.Context) {
 	fileWm, err := thumb.Thumb()
 
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
 		return
 	}
 	defer fileWm.Destroy()
 	//不写入文件,直接返回给浏览器
 	_, err = ctx.Writer.Write(fileWm.GetImageBlob())
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("ctx.Writer.Write err: %s", err.Error()), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("ctx.Writer.Write err: %s", err.Error()), []interface{}{})
 		return
 	}
 }
@@ -41,14 +44,14 @@ func Preview(ctx *gin.Context) {
 func Image(ctx *gin.Context) {
 	jsonByte, err := os.ReadFile("data.json")
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
 		return
 	}
 	var posterJson image.PosterJson
 	err = json.Unmarshal(jsonByte, &posterJson)
 
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
 		return
 	}
 
@@ -56,12 +59,12 @@ func Image(ctx *gin.Context) {
 
 	file, err := poster.Create(posterJson)
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("poster.Create err : %s", err.Error()), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("poster.Create err : %s", err.Error()), []interface{}{})
 		return
 	}
 	if err = file.SetImageFormat("PNG"); err != nil {
 
-		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("file.SetImageFormat err : %s", err.Error()), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("file.SetImageFormat err : %s", err.Error()), []interface{}{})
 		return
 	}
 	defer file.Destroy()
@@ -69,31 +72,22 @@ func Image(ctx *gin.Context) {
 		posterJson.Quality = 50
 	}
 	if err = file.SetCompressionQuality(uint(posterJson.Quality)); err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("file.SetCompressionQuality err: %s", err.Error()), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("file.SetCompressionQuality err: %s", err.Error()), []interface{}{})
 		return
 	}
 	//不写入文件,直接返回给浏览器
 	_, err = ctx.Writer.Write(file.GetImageBlob())
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("ctx.Writer.Write err: %s", err.Error()), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("ctx.Writer.Write err: %s", err.Error()), []interface{}{})
 		return
 	}
-	/*
-		stream, err := os.ReadFile(file)
-		if err != nil {
-			fmt.Println("read error")
-			ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
-			return
-		}
-
-		ctx.Writer.WriteString(string(stream))
-	*/
 }
 
+// Cut 裁切指定的图片
 func Cut(ctx *gin.Context) {
 	file := ctx.Query("file")
 	if file == "" {
-		ResponseError(ctx, entity.CodePageNotFound)
+		entity.ResponseError(ctx, entity.CodePageNotFound)
 		return
 	}
 
@@ -105,14 +99,14 @@ func Cut(ctx *gin.Context) {
 	fileWm, err := thumb.Cut("center")
 
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
 		return
 	}
 	defer fileWm.Destroy()
 	//不写入文件,直接返回给浏览器
 	_, err = ctx.Writer.Write(fileWm.GetImageBlob())
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("ctx.Writer.Write err: %s", err.Error()), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("ctx.Writer.Write err: %s", err.Error()), []interface{}{})
 		return
 	}
 }
@@ -121,17 +115,18 @@ func SavePoster(ctx *gin.Context) {
 
 }
 
+// PosterInfo 获取指定posterID的海报信息
 func PosterInfo(ctx *gin.Context) {
 	var request entity.PosterInfoRequest
 	if err := ctx.ShouldBind(&request); err != nil {
-		ResponseError(ctx, entity.CodeInvalidParam)
+		entity.ResponseError(ctx, entity.CodeInvalidParam)
 		return
 	}
 	sUserPoster := model.NewUserPoster(ctx)
 	info, err := sUserPoster.Info(request.PosterID)
 
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeServerBusy, info.Msg, nil)
+		entity.ResponseNormal(ctx, entity.CodeServerBusy, info.Msg, nil)
 		return
 	}
 	var api entity.UserPosterInfoForApi
@@ -139,57 +134,85 @@ func PosterInfo(ctx *gin.Context) {
 	api.Msg = info.Msg
 	api.Body.Poster = info.Body
 
-	ResponseNormal(ctx, api.Code, api.Msg, api.Body)
+	entity.ResponseNormal(ctx, api.Code, api.Msg, api.Body)
 }
 
+// DrawImage 生成指定posterID的海报
 func DrawImage(ctx *gin.Context) {
+	sinceStart := time.Now()
 	var request entity.PosterInfoRequest
 	if err := ctx.ShouldBind(&request); err != nil {
-		ResponseError(ctx, entity.CodeInvalidParam)
+		entity.ResponseError(ctx, entity.CodeInvalidParam)
 		return
 	}
 	sUserPoster := model.NewUserPoster(ctx)
 	info, err := sUserPoster.Info(request.PosterID)
 
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeServerBusy, info.Msg, nil)
+		entity.ResponseNormal(ctx, entity.CodeServerBusy, info.Msg, nil)
 		return
 	}
+	spendTime := time.Since(sinceStart)
+	fmt.Println("查询数据耗时:", spendTime)
 
+	startTime := time.Now()
 	var posterJson image.PosterJson
 	err = json.Unmarshal([]byte(info.Body.PosterJson), &posterJson)
 
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, err.Error(), []interface{}{})
 		return
 	}
-
+	endTime := time.Since(startTime)
+	fmt.Println("解压数据耗时:", endTime)
 	poster := image.NewPoster(ctx)
 
 	file, err := poster.Create(posterJson)
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("poster.Create err : %s", err.Error()), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("poster.Create err : %s", err.Error()), []interface{}{})
 		return
 	}
 	if err = file.SetImageFormat("PNG"); err != nil {
 
-		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("file.SetImageFormat err : %s", err.Error()), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("file.SetImageFormat err : %s", err.Error()), []interface{}{})
 		return
 	}
 	defer file.Destroy()
+	defer util.TimeCost("渲染数据消耗")()
 	if posterJson.Quality == 0 {
 		posterJson.Quality = 50
 	}
-	posterJson.Quality = 30
 	if err = file.SetCompressionQuality(uint(posterJson.Quality)); err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("file.SetCompressionQuality err: %s", err.Error()), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("file.SetCompressionQuality err: %s", err.Error()), []interface{}{})
 		return
 	}
 
 	//不写入文件,直接返回给浏览器
 	_, err = ctx.Writer.Write(file.GetImageBlob())
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("ctx.Writer.Write err: %s", err.Error()), []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeSystemError, fmt.Sprintf("ctx.Writer.Write err: %s", err.Error()), []interface{}{})
+		return
+	}
+}
+
+// Posters 通过用户ID获取用户名下的所有海报
+func Posters(ctx *gin.Context) {
+	uid := utString.ConvertInt64(ctx.GetString("uid"))
+
+	if uid == 0 {
+		entity.ResponseNormal(ctx, entity.CodeDenied, "请登录后再试", []interface{}{})
 		return
 	}
+
+	mUserPoster := model.NewUserPoster(ctx)
+
+	simpleLists := mUserPoster.SimpleLists(uid)
+	userPostersForApi := &entity.UserPostersForApi{
+		Code: 0,
+		Msg:  "success",
+		Body: map[string][]entity.PostersListForApi{
+			"posters": simpleLists,
+		},
+	}
+	entity.ResponseSuccess(ctx, userPostersForApi)
 }

+ 0 - 71
controller/response.go

@@ -1,71 +0,0 @@
-package controller
-
-import (
-	"github.com/gin-gonic/gin"
-	"icloudapp.cn/tools/entity"
-	"net/http"
-)
-
-/*
-{
-	"code": 10000, // 程序中的错误码
-	"msg": xx,     // 提示信息
-	"data": {},    // 数据
-}
-*/
-
-type ResponseData struct {
-	Code entity.ResCode `json:"code"`
-	Msg  interface{}    `json:"msg"`
-	Body interface{}    `json:"data,omitempty"`
-}
-
-type ResponseBodyData struct {
-	Code entity.ResCode `json:"code"`
-	Msg  interface{}    `json:"msg"`
-	Body interface{}    `json:"body:omitempty"`
-}
-
-// Response error
-func ResponseError(c *gin.Context, code entity.ResCode) {
-	c.JSON(http.StatusOK, &ResponseData{
-		Code: code,
-		Msg:  code.Msg(),
-		Body: nil,
-	})
-}
-
-// Response error with msg
-func ResponseErrorWithMsg(c *gin.Context, code entity.ResCode, msg interface{}) {
-	c.JSON(http.StatusOK, &ResponseData{
-		Code: code,
-		Msg:  msg,
-		Body: nil,
-	})
-}
-
-// Response success
-func ResponseSuccess(c *gin.Context, data interface{}) {
-	c.JSON(http.StatusOK, &ResponseData{
-		Code: entity.CodeSuccess,
-		Msg:  entity.CodeSuccess.Msg(),
-		Body: data,
-	})
-}
-
-// Response {code, msg, body}
-func ResponseNormal(c *gin.Context, code entity.ResCode, msg interface{}, body interface{}) {
-	c.JSON(http.StatusOK, &ResponseData{
-		Code: code,
-		Msg:  msg,
-		Body: body,
-	})
-}
-
-func ResponseBody(c *gin.Context, code entity.ResCode, msg interface{}, body interface{}) {
-	c.JSON(http.StatusOK, &ResponseBodyData{
-		Code: code,
-		Msg:  msg,
-		Body: body,
-	})
-}

+ 121 - 14
controller/user.go

@@ -2,49 +2,65 @@ package controller
 
 import (
 	"github.com/gin-gonic/gin"
+	setting "icloudapp.cn/tools/config"
 	"icloudapp.cn/tools/entity"
+	tError "icloudapp.cn/tools/errors"
 	"icloudapp.cn/tools/model"
-	string2 "icloudapp.cn/tools/util/string"
+	"icloudapp.cn/tools/service"
+	"icloudapp.cn/tools/util"
+	"time"
 )
 
 // Info 获取用户信息
 func Info(ctx *gin.Context) {
-	uid := string2.ConvertInt64(ctx.Query("uid"))
+	uid := ctx.GetInt64("uid")
 
 	if uid < 1 {
-		ResponseError(ctx, entity.CodeInvalidParam)
+		entity.ResponseError(ctx, entity.CodeInvalidParam)
 		return
 	}
 	mUser := model.NewUser(ctx)
 	userInfo, err := mUser.Info(uid)
 
 	if err != nil {
-		ResponseNormal(ctx, userInfo.Code, userInfo.Msg, nil)
+		entity.ResponseNormal(ctx, userInfo.Code, userInfo.Msg, nil)
 		return
 	}
+	token := service.GetJWTTokenFromRedis(userInfo.Body.Uid)
+	responseAPIData := entity.UserInfoAPIData{
+		Uid:      userInfo.Body.Uid,
+		Username: userInfo.Body.Username,
+		Type:     1,
+		Status:   userInfo.Body.Status,
+		Token:    token,
+		Avatar:   userInfo.Body.Avatar,
+	}
 
-	ResponseSuccess(ctx, userInfo.Body)
+	entity.ResponseSuccess(ctx, responseAPIData)
 }
 
 // Login 登录
 func Login(ctx *gin.Context) {
 	var request entity.LoginRequest
 	if err := ctx.ShouldBind(&request); err != nil {
-		ResponseError(ctx, entity.CodeInvalidParam)
+		entity.ResponseError(ctx, entity.CodeInvalidParam)
 		return
 	}
 	mUser := model.NewUser(ctx)
 	loginInfo, err := mUser.Login(request.UserName, request.Password)
 
 	if err != nil {
-		ResponseNormal(ctx, entity.CodeServerBusy, "系统错误", nil)
+		entity.ResponseNormal(ctx, entity.CodeServerBusy, err.Error(), nil)
+		return
 	}
 
 	if loginInfo.Uid < 1 {
-		ResponseNormal(ctx, entity.CodeDenied, "登录失败", []interface{}{})
+		entity.ResponseNormal(ctx, entity.CodeDenied, "登录失败", []interface{}{})
 		return
 	}
-	ResponseSuccess(ctx, loginInfo)
+	//设置cookie
+	util.NewPosterCookie(ctx).SetCookie("poster_sid", loginInfo.Token, int(setting.Conf.JWTConfig.ExpireAt))
+	entity.ResponseSuccess(ctx, loginInfo)
 }
 
 // Register 注册
@@ -52,7 +68,7 @@ func Register(ctx *gin.Context) {
 	var request entity.RegisterRequest
 
 	if err := ctx.ShouldBind(&request); err != nil {
-		ResponseError(ctx, entity.CodeInvalidParam)
+		entity.ResponseError(ctx, entity.CodeInvalidParam)
 		return
 	}
 
@@ -61,13 +77,104 @@ func Register(ctx *gin.Context) {
 	userInfo, err := mUser.Register(request.UserName, request.Password, request.Email)
 
 	if err != nil {
-		ResponseNormal(ctx, userInfo.Code, err.Error(), []interface{}{})
+		entity.ResponseNormal(ctx, userInfo.Code, err.Error(), entity.EmptyBodyObject())
 		return
 	}
 	if userInfo.Code > 0 {
-		//ResponseNormal(ctx, entity.CodeDenied, userInfo.Msg, userInfo.Body)
-		ResponseNormal(ctx, userInfo.Code, userInfo.Msg, []interface{}{})
+		entity.ResponseNormal(ctx, userInfo.Code, userInfo.Msg, entity.EmptyBodyObject())
+		return
+	}
+	entity.ResponseSuccess(ctx, userInfo)
+}
+
+func ChangePassword(ctx *gin.Context) {
+	var request entity.ChangePasswordRequest
+
+	if err := ctx.ShouldBind(&request); err != nil {
+		entity.ResponseError(ctx, entity.CodeInvalidParam)
+		return
+	}
+	/*cookie, err := ctx.Cookie("poster_sid")
+
+	if err != nil {
+		entity.ResponseNormal(ctx, entity.CodeNotAllowed, err.Error(), entity.EmptyBodyObject())
 		return
 	}
-	ResponseSuccess(ctx, userInfo)
+	claims, err := service.ParseJWTToken(cookie)
+
+	if err != nil {
+		entity.ResponseNormal(ctx, entity.CodeNotAllowed, err.Error(), entity.EmptyBodyObject())
+		return
+	}*/
+	uid := GetUID(ctx)
+	mUser := model.NewUser(ctx)
+	if uid == 0 {
+		entity.ResponseNormal(ctx, entity.CodeDenied, "请先登录后再试", entity.EmptyBodyObject())
+		return
+	}
+	userInfo, err := mUser.ChangePassword(uid, request.Password)
+
+	if err != nil {
+		entity.ResponseNormal(ctx, entity.CodeSystemError, err.Error(), entity.EmptyBodyObject())
+		return
+	}
+
+	entity.ResponseSuccess(ctx, userInfo.Body)
+}
+
+func RefreshToken(ctx *gin.Context) {
+	uid := GetUID(ctx)
+	username := ctx.GetString("username")
+
+	token, err := service.GenJWTToken(uid, username)
+	if err != nil {
+		entity.ResponseNormal(ctx, entity.CodeSystemError, err.Error(), entity.EmptyBodyObject())
+		return
+	}
+	expire := time.Now().Add(service.JWTExpireDuration()).Unix()
+
+	refresh := map[string]interface{}{
+		"token":     token,
+		"expire":    expire,
+		"expire_at": time.Unix(expire, 0),
+	}
+
+	util.NewPosterCookie(ctx).SetCookie("poster_sid", token, int(setting.Conf.JWTConfig.ExpireAt))
+	entity.ResponseSuccess(ctx, refresh)
+}
+func Logout(ctx *gin.Context) {
+	//移除用户登录信息
+	uid := GetUID(ctx)
+	err := service.RemoveJWTToken(uid)
+	if err != nil {
+		entity.ResponseNormal(ctx, entity.CodeSystemError, err.Error(), entity.EmptyBodyObject())
+		return
+	}
+	util.NewPosterCookie(ctx).SetCookie("poster_sid", "", int(setting.Conf.EncryptConfig.ExpireAt))
+}
+
+func ChangeEmail(ctx *gin.Context) {
+	var request entity.ChangeEmailRequest
+
+	if err := ctx.ShouldBind(&request); err != nil {
+		if !HandleNext(ctx, tError.NewTError(entity.CodeInvalidParam.Code(), "参数不正确")) {
+			return
+		}
+	}
+
+	uid := GetUID(ctx)
+
+	if uid == 0 {
+		if !HandleNext(ctx, tError.NewTError(entity.CodeDenied.Code(), "请登录后重试")) {
+			return
+		}
+	}
+	userInfo, err := model.NewUser(ctx).ChangeEmail(uid, request.Email)
+
+	if err != nil {
+		if !HandleNext(ctx, tError.NewTError(entity.CodeSystemError.Code(), err.Error())) {
+			return
+		}
+	}
+	entity.ResponseSuccess(ctx, userInfo.Body)
 }

+ 17 - 0
entity/code.go

@@ -24,6 +24,14 @@ const (
 
 	CodeSystemError      ResCode = 1011
 	CodeDataDoesNotExist ResCode = 1012
+
+	CodeAuthIsNull ResCode = 1013
+	CodeAuthFail   ResCode = 1014
+
+	CodeCreateDirFail ResCode = 1015
+	CodeUploadFail    ResCode = 1016
+
+	CodeTokenExpired ResCode = 1017
 )
 
 var codeMsgMap = map[ResCode]string{
@@ -43,6 +51,11 @@ var codeMsgMap = map[ResCode]string{
 	CodeFileNotExist:     "文件不存在",
 	CodeSystemError:      "系统错误",
 	CodeDataDoesNotExist: "未找到相关数据",
+	CodeAuthIsNull:       "Auth Code 不能为空",
+	CodeAuthFail:         "Auth fail",
+	CodeCreateDirFail:    "创建目录失败",
+	CodeUploadFail:       "文件上传失败",
+	CodeTokenExpired:     "Token已过期",
 }
 
 type Code struct {
@@ -67,3 +80,7 @@ func (c ResCode) Msg() string {
 func (c ResCode) Error() error {
 	return errors.New(c.Msg())
 }
+
+func (c ResCode) Code() int {
+	return int(c)
+}

+ 98 - 0
entity/response.go

@@ -0,0 +1,98 @@
+package entity
+
+import (
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
+
+/*
+{
+	"code": 10000, // 程序中的错误码
+	"msg": xx,     // 提示信息
+	"data": {},    // 数据
+}
+*/
+
+type ResponseData struct {
+	Code ResCode     `json:"code"`
+	Msg  interface{} `json:"msg"`
+	Body interface{} `json:"data,omitempty"`
+}
+
+type ResponseBodyData struct {
+	Code ResCode     `json:"code"`
+	Msg  interface{} `json:"msg"`
+	Body interface{} `json:"body,omitempty"`
+}
+
+type ResponseNormalBody struct {
+	Code int         `json:"code"`
+	Msg  interface{} `json:"msg"`
+	Body interface{} `json:"body,omitempty"`
+}
+
+type ResponseNormalData struct {
+	Code int         `json:"code"`
+	Msg  interface{} `json:"msg"`
+	Body interface{} `json:"data,omitempty"`
+}
+
+// Response error
+func ResponseError(c *gin.Context, code ResCode) {
+	c.JSON(http.StatusOK, &ResponseData{
+		Code: code,
+		Msg:  code.Msg(),
+		Body: nil,
+	})
+}
+
+// Response error with msg
+func ResponseErrorWithMsg(c *gin.Context, code ResCode, msg interface{}) {
+	c.JSON(http.StatusOK, &ResponseData{
+		Code: code,
+		Msg:  msg,
+		Body: nil,
+	})
+}
+
+// Response success
+func ResponseSuccess(c *gin.Context, data interface{}) {
+	c.JSON(http.StatusOK, &ResponseData{
+		Code: CodeSuccess,
+		Msg:  CodeSuccess.Msg(),
+		Body: data,
+	})
+}
+
+// Response {code, msg, body}
+func ResponseNormal(c *gin.Context, code ResCode, msg interface{}, body interface{}) {
+	c.JSON(http.StatusOK, &ResponseData{
+		Code: code,
+		Msg:  msg,
+		Body: body,
+	})
+}
+
+func ResponseBody(c *gin.Context, code ResCode, msg interface{}, body interface{}) {
+	c.JSON(http.StatusOK, &ResponseBodyData{
+		Code: code,
+		Msg:  msg,
+		Body: body,
+	})
+}
+
+func ResponseHandleBody(c *gin.Context, code int, msg interface{}, body interface{}) {
+	c.JSON(http.StatusOK, &ResponseNormalBody{
+		Code: code,
+		Msg:  msg,
+		Body: body,
+	})
+}
+
+func EmptyBodyObject() map[string]interface{} {
+	return map[string]interface{}{}
+}
+
+func EmptyBodyArray() []interface{} {
+	return []interface{}{}
+}

+ 19 - 2
entity/user_poster.go

@@ -5,6 +5,7 @@ import "time"
 type PosterInfoRequest struct {
 	PosterID int64 `form:"id" binding:"required"`
 }
+
 type UserPosterInfo struct {
 	Code ResCode
 	Msg  string
@@ -18,6 +19,22 @@ type UserPosterInfoForApi struct {
 		Poster UserPoster `json:"poster"`
 	}
 }
+
+type UserPostersForApi struct {
+	Code ResCode                        `json:"code"`
+	Msg  string                         `json:"msg"`
+	Body map[string][]PostersListForApi `json:"body"`
+}
+
+type PostersListForApi struct {
+	Id       int64     `json:"id"`
+	Name     string    `json:"name"`
+	Preview  string    `json:"preview"`
+	Status   int64     `json:"status"`
+	Visits   int64     `json:"visits"`
+	CreateAt time.Time `json:"createTime"`
+}
+
 type UserPoster struct {
 	PosterID       int64     `gorm:"column:poster_id" json:"id"`
 	Name           string    `gorm:"column:poster_name" json:"name"`
@@ -27,9 +44,9 @@ type UserPoster struct {
 	PosterJson     string    `gorm:"column:poster_json" json:"json"`
 	Preview        string    `gorm:"column:preview" json:"preview"`
 	Visit          string    `gorm:"column:visit" json:"visit"`
-	Status         string    `gorm:"column:status" json:"status"`
+	Status         string    `gorm:"column:status;default:1" json:"status"`
 	CreateAt       time.Time `gorm:"column:create_at" json:"create_at"`
-	UpdateAt       time.Time `gorm:"column:update_at" json:"update_at"`
+	UpdateAt       time.Time `gorm:"column:update_at;default:CURRENT_TIMESTAMP" json:"update_at"`
 }
 
 type PosterJson struct {

+ 41 - 11
entity/userinfo.go

@@ -2,32 +2,53 @@ package entity
 
 import "time"
 
-type Login struct {
-	Uid      int64  `json:"uid"`
-	Username string `json:"username"`
-	Type     int    `json:"type"`
-	Avatar   string `json:"avatar"`
-	Status   int8   `json:"status"`
-	Token    string `json:"token"`
-}
-
+// LoginRequest 登录请求参数
 type LoginRequest struct {
 	UserName string `form:"username" binding:"required"`
 	Password string `form:"password" binding:"required"`
 }
 
+// Login 登录返回数据结构
+type Login struct {
+	Uid       int64     `json:"uid"`
+	Username  string    `json:"username"`
+	Type      int       `json:"type"`
+	Avatar    string    `json:"avatar"`
+	Status    int8      `json:"status"`
+	Token     string    `json:"token"`
+	Expire    int64     `json:"expire"`
+	ExpiresAt time.Time `json:"expires_at"`
+}
+
+// RegisterRequest 注册请求参数
 type RegisterRequest struct {
 	UserName string `form:"username" binding:"required"`
 	Password string `form:"password" binding:"required"`
 	Email    string `form:"email"`
 }
 
+// UserInfo 返回给接口的用户信息,带Code和Msg
 type UserInfo struct {
 	Code ResCode
 	Msg  string
 	Body User
 }
 
+type UserInfoAPI struct {
+	Code ResCode         `json:"code"`
+	Msg  string          `json:"msg"`
+	Body UserInfoAPIData `json:"data"`
+}
+type UserInfoAPIData struct {
+	Uid      int64  `json:"uid"`
+	Username string `json:"username"`
+	Type     int    `json:"type"`
+	Status   int8   `json:"status"`
+	Token    string `json:"token"`
+	Avatar   string `json:"avatar"`
+}
+
+// User 用户信息
 type User struct {
 	Uid      int64     `gorm:"column:uid" json:"uid"`
 	Username string    `gorm:"column:nickname" json:"username"`
@@ -35,7 +56,16 @@ type User struct {
 	Password string    `gorm:"column:password" json:"password"`
 	Avatar   string    `gorm:"column:avatar" json:"avatar"`
 	Salt     string    `gorm:"column:salt" json:"salt"`
-	Status   int8      `gorm:"column:status" json:"status"`
+	Status   int8      `gorm:"column:status;default:1" json:"status"`
 	CreateAt time.Time `gorm:"column:create_at" json:"create_at"`
-	UpdateAt time.Time `gorm:"column:update_at" json:"update_at"`
+	UpdateAt time.Time `gorm:"column:update_at;default:CURRENT_TIMESTAMP" json:"update_at"`
+}
+
+// ChangePasswordRequest 修改密码提交的数据,用户id从jwt的中间件中获取
+type ChangePasswordRequest struct {
+	Password string `form:"password" binding:"required"`
+}
+
+type ChangeEmailRequest struct {
+	Email string `form:"email" binding:"required"`
 }

+ 12 - 4
go.mod

@@ -3,7 +3,9 @@ module icloudapp.cn/tools
 go 1.19
 
 require (
+	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/fsnotify/fsnotify v1.6.0
+	github.com/gin-contrib/gzip v0.0.6
 	github.com/gin-gonic/gin v1.9.0
 	github.com/go-redis/redis v6.15.9+incompatible
 	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
@@ -14,7 +16,9 @@ require (
 	github.com/spf13/viper v1.15.0
 	gopkg.in/gographics/imagick.v3 v3.4.2
 	gorm.io/driver/mysql v1.4.7
-	gorm.io/gorm v1.24.5
+	gorm.io/gen v0.3.21
+	gorm.io/gorm v1.24.6
+	gorm.io/plugin/dbresolver v1.4.1
 )
 
 require (
@@ -52,10 +56,14 @@ require (
 	github.com/ugorji/go/codec v1.2.9 // indirect
 	golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
 	golang.org/x/crypto v0.5.0 // indirect
-	golang.org/x/net v0.7.0 // indirect
-	golang.org/x/sys v0.5.0 // indirect
-	golang.org/x/text v0.7.0 // indirect
+	golang.org/x/mod v0.9.0 // indirect
+	golang.org/x/net v0.8.0 // indirect
+	golang.org/x/sys v0.6.0 // indirect
+	golang.org/x/text v0.8.0 // indirect
+	golang.org/x/tools v0.7.0 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
+	gorm.io/datatypes v1.1.1 // indirect
+	gorm.io/hints v1.1.1 // indirect
 )

+ 1 - 2
middleware/auth.go

@@ -2,7 +2,6 @@ package middleware
 
 import (
 	"github.com/gin-gonic/gin"
-	"icloudapp.cn/tools/controller"
 	"icloudapp.cn/tools/entity"
 )
 
@@ -13,7 +12,7 @@ func AuthMiddleware() func(c *gin.Context) {
 		// token验证成功,返回c.Next继续,否则返回c.Abort()直接返回
 		authHeader := c.Request.Header.Get("Authorization")
 		if authHeader == "" {
-			controller.ResponseError(c, entity.CodeNeedLogin)
+			entity.ResponseError(c, entity.CodeNeedLogin)
 			c.Abort()
 			return
 		}

+ 18 - 10
model/user.go

@@ -4,13 +4,10 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	setting "icloudapp.cn/tools/config"
 	"icloudapp.cn/tools/entity"
 	_ "icloudapp.cn/tools/repository/mysql"
 	"icloudapp.cn/tools/service"
 	"icloudapp.cn/tools/util"
-	"icloudapp.cn/tools/util/url"
-	"strconv"
 	"time"
 )
 
@@ -36,18 +33,22 @@ func (u *User) Login(username, password string) (entity.Login, error) {
 		return login, errors.New("密码不匹配")
 	}
 
-	encodeStr := url.HttpBuildQuery(map[string]string{
+	/*encodeStr := url.HttpBuildQuery(map[string]string{
 		"uid":      strconv.FormatInt(userInfo.Uid, 10),
 		"username": userInfo.Username,
 	})
 
-	token, _ := util.EncryptWidthTime(encodeStr, setting.Conf.Encrypt.ExpireAt, setting.Conf.Encrypt.Key)
+	token, _ := util.EncryptWidthTime(encodeStr, setting.Conf.EncryptConfig.ExpireAt, setting.Conf.EncryptConfig.Key)
+	*/
+	token, _ := service.GenJWTToken(userInfo.Uid, userInfo.Username)
 	login.Uid = userInfo.Uid
 	login.Username = userInfo.Username
 	login.Status = userInfo.Status
 	login.Avatar = userInfo.Avatar
 	login.Type = 1
 	login.Token = token
+	login.Expire = time.Now().Add(service.JWTExpireDuration()).Unix()
+	login.ExpiresAt = time.Unix(login.Expire, 0)
 	return login, nil
 }
 
@@ -62,9 +63,6 @@ func (u *User) Register(username, password, email string) (entity.UserInfo, erro
 	}
 	register.Email = email
 	register.Status = 1
-	timeParse, _ := time.Parse(time.Now().String(), "2006-01-02 15:04:05")
-
-	fmt.Println("timeParse", timeParse, time.Unix(time.Now().Unix(), 0).Format("2006-01-02 15:04"))
 	register.CreateAt = time.Now()
 	register.UpdateAt = time.Now()
 
@@ -74,9 +72,19 @@ func (u *User) Register(username, password, email string) (entity.UserInfo, erro
 
 func (u *User) Info(uid int64) (entity.UserInfo, error) {
 	sUser := service.NewUser(u.ctx)
-
 	return sUser.UserInfoByID(uid)
 }
-func (*User) ChangePassword(uid int64, password string) {
 
+// ChangePassword 通过uid修改密码
+func (u *User) ChangePassword(uid int64, password string) (entity.UserInfo, error) {
+	sUser := service.NewUser(u.ctx)
+	salt := util.RandomNumberStr(4)
+	password = util.Sign(password, salt)
+	return sUser.ChangePassword(uid, password, salt)
+}
+
+// ChangeEmail 更新邮件地址
+func (u *User) ChangeEmail(uid int64, email string) (entity.UserInfo, error) {
+	sUser := service.NewUser(u.ctx)
+	return sUser.ChangeEmail(uid, email)
 }

+ 22 - 2
model/user_poster.go

@@ -15,11 +15,31 @@ func NewUserPoster(ctx context.Context) *UserPoster {
 }
 
 func (p *UserPoster) Info(posterID int64) (entity.UserPosterInfo, error) {
-	sPoster := service.NewPoster(p.ctx)
+	sPoster := service.NewUserPoster(p.ctx)
 	return sPoster.InfoByID(posterID)
 }
 
 func (p *UserPoster) InfoByUUID(uuid int64) (entity.UserPosterInfo, error) {
-	sPoster := service.NewPoster(p.ctx)
+	sPoster := service.NewUserPoster(p.ctx)
 	return sPoster.InfoByUUID(uuid)
 }
+
+func (p *UserPoster) SimpleLists(uid int64) []entity.PostersListForApi {
+	sPoster := service.NewUserPoster(p.ctx)
+	lists := sPoster.SimpleLists(uid)
+
+	res := make([]entity.PostersListForApi, 0)
+
+	for _, v := range lists {
+		var poster entity.PostersListForApi
+		poster.Id = v.PosterID
+		poster.Name = v.PosterName
+		poster.Preview = *v.Preview
+		poster.Visits = v.Visits
+		poster.Status = v.Status
+		poster.CreateAt = v.CreateAt
+		res = append(res, poster)
+	}
+
+	return res
+}

+ 42 - 0
repository/mysql/mysql.go

@@ -5,7 +5,11 @@ import (
 	"fmt"
 	"gorm.io/driver/mysql"
 	"gorm.io/gorm"
+	"gorm.io/gorm/logger"
+	"gorm.io/plugin/dbresolver"
 	setting "icloudapp.cn/tools/config"
+	"icloudapp.cn/tools/service/entity"
+	"icloudapp.cn/tools/service/model"
 )
 
 var DBConn *gorm.DB
@@ -19,13 +23,51 @@ func Init(cfg *setting.MySQLConfig) {
 	}
 	sqlDB.SetMaxOpenConns(cfg.MaxOpenConns) //最大连接数
 	sqlDB.SetMaxOpenConns(cfg.MaxIdleConns)
+
+	loggerLevel := logger.Silent
+	if setting.Conf.Mode == "dev" {
+		loggerLevel = logger.Info
+	}
 	gormDB, err := gorm.Open(mysql.New(mysql.Config{
 		Conn: sqlDB,
 	}), &gorm.Config{
 		DisableForeignKeyConstraintWhenMigrating: true, //禁用外键生成
+		Logger:                                   logger.Default.LogMode(loggerLevel),
 	})
+
 	if err != nil {
 		panic(fmt.Sprintf("链接数据库失败\v", err))
 	}
+
+	// db resolver
+
+	var material *entity.Material
+	var group *entity.Group
+	var user *entity.User
+
+	errDbresolver := gormDB.Use(dbresolver.Register(dbresolver.Config{
+		Sources:           []gorm.Dialector{open(cfg)},
+		Replicas:          []gorm.Dialector{open(cfg), open(cfg)},
+		Policy:            dbresolver.RandomPolicy{},
+		TraceResolverMode: true,
+	}).Register(dbresolver.Config{
+		Replicas:          []gorm.Dialector{open(setting.Conf.ReplicasForUser)},
+		TraceResolverMode: true,
+	}, user).Register(dbresolver.Config{
+		Sources:           []gorm.Dialector{open(setting.Conf.ReplicasForOrder), open(setting.Conf.ReplicasForOrder)},
+		Replicas:          []gorm.Dialector{open(setting.Conf.ReplicasForOrder)},
+		TraceResolverMode: true,
+	}, group, material))
+
+	if err != nil {
+		panic("dbResolver err :" + errDbresolver.Error())
+	}
+
 	DBConn = gormDB
+	model.SetDefault(DBConn)
+}
+func open(cfg *setting.MySQLConfig) gorm.Dialector {
+	dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
+		cfg.User, cfg.Password, cfg.Host, cfg.DB)
+	return mysql.Open(dsn)
 }

+ 31 - 7
router/api_router.go

@@ -2,11 +2,15 @@ package router
 
 import (
 	"github.com/gin-gonic/gin"
+	"icloudapp.cn/tools/admin"
 	"icloudapp.cn/tools/controller"
 	"icloudapp.cn/tools/middleware"
+	"net/http"
 )
 
 func SetupApiRouters(r *gin.Engine) {
+	r.StaticFS("/upload", http.Dir("./upload"))
+
 	v1 := r.Group("/api/v1")
 	v1.Use(middleware.AuthMiddleware())
 	/*
@@ -18,19 +22,39 @@ func SetupApiRouters(r *gin.Engine) {
 	*/
 	r.GET("/index", controller.Index)
 
-	user := r.Group("/api/user")
-	user.GET("/info", controller.Info)
-	user.GET("/login", controller.Login)
-	user.POST("/login", controller.Login)
+	userApi := r.Group("/api/user")
+	userApi.GET("/login", controller.Login)
+	userApi.POST("/login", controller.Login)
+	userApi.GET("/register", controller.Register)
+	userApi.POST("/register", controller.Register)
+
+	userApiNeedLogin := r.Group("api/user")
+	userApiNeedLogin.Use(middleware.JWTAuthMiddleware())
+	userApiNeedLogin.GET("/info", controller.Info)
+	userApiNeedLogin.GET("/refresh_token", controller.RefreshToken)
+
+	userApiNeedLogin.GET("/changepwd", controller.ChangePassword)
+	userApiNeedLogin.POST("/changepwd", controller.ChangePassword)
 
-	user.GET("/register", controller.Register)
-	user.POST("/register", controller.Register)
+	userApiNeedLogin.GET("/change_email", controller.ChangeEmail)
+	userApiNeedLogin.POST("/change_email", controller.ChangeEmail)
 
 	poster := r.Group("/api/poster")
 	poster.GET("/info", controller.PosterInfo)
 	poster.GET("/image", controller.Image)
 	poster.GET("/cut", controller.Cut)
-	poster.POST("/save", controller.SavePoster)
+	poster.GET("/save", middleware.JWTAuthMiddleware(), controller.SavePoster)
 	poster.GET("/preview", controller.Preview)
 	poster.GET("/draw", controller.DrawImage)
+
+	adminApi := r.Group("api/admin")
+	adminApi.Use(middleware.JWTAuthMiddleware())
+	adminApi.Use(middleware.RoleMiddleWare())
+	adminApi.POST("/upload/material", admin.Material)
+	adminApi.POST("/upload/fonts", admin.Font)
+
+	uploadApi := r.Group("/api/upload")
+	uploadApi.POST("/material", middleware.RoleMiddleWare(), admin.Material)
+	uploadApi.POST("/fonts", middleware.RoleMiddleWare(), admin.Font)
+
 }

+ 15 - 0
router/init.go

@@ -1,13 +1,28 @@
 package router
 
 import (
+	"fmt"
+	"github.com/gin-contrib/gzip"
 	"github.com/gin-gonic/gin"
 	"icloudapp.cn/tools/middleware"
+	"net/http"
 )
 
 func InitRouter() *gin.Engine {
 	r := gin.Default()
+	// 上传大小限制 100M
+	r.MaxMultipartMemory = 100 << 20
+	r.Use(gzip.Gzip(gzip.BestCompression))
 	r.Use(middleware.LoggerMiddleware())
+	//注册错误恢复
+	r.Use(gin.CustomRecovery(func(ctx *gin.Context, recovered interface{}) {
+		if err, ok := recovered.(string); ok {
+			ctx.String(http.StatusInternalServerError, fmt.Sprintf("error occuring: %s", err))
+		}
+		ctx.Abort()
+		//c.AbortWithStatus(http.StatusInternalServerError)
+	}))
 	SetupApiRouters(r)
+	SetupPHPRouter(r)
 	return r
 }

+ 67 - 2
service/user.go

@@ -11,13 +11,20 @@ import (
 )
 
 type User struct {
-	ctx context.Context
+	ctx  context.Context
+	Role string
 }
 
 func NewUser(ctx context.Context) *User {
-	return &User{ctx}
+	return &User{ctx, ""}
 }
 
+// SetRole 设置用户权限
+func (u *User) SetRole(role string) {
+	u.Role = role
+}
+
+// TableName 设置表名称
 func (u *User) TableName() string {
 	return "user"
 }
@@ -128,3 +135,61 @@ func (u *User) Register(register entity.User) (userInfo entity.UserInfo, err err
 	userInfo.Body = register
 	return userInfo, nil
 }
+
+// ChangePassword 通过用户ID更改密码
+func (u *User) ChangePassword(uid int64, password, slat string) (userInfo entity.UserInfo, err error) {
+	userInfo, errInfo := u.UserInfoByID(uid)
+
+	if err != nil {
+		userInfo.Msg = entity.CodeServerBusy.Msg()
+		userInfo.Code = entity.CodeServerBusy
+		return userInfo, errors.New(errInfo.Error())
+	}
+	var user entity.User
+	user.Uid = uid
+	user.Salt = slat
+	user.Status = userInfo.Body.Status
+	user.Password = password
+
+	result := mysql.DBConn.Table(u.TableName()).Where("uid = ?", uid).UpdateColumns(&user)
+
+	if result.Error != nil {
+		return userInfo, result.Error
+	}
+
+	return userInfo, nil
+}
+
+// ChangeEmail 更换邮件地址,邮件地址必须是唯一的
+func (u *User) ChangeEmail(uid int64, email string) (userInfo entity.UserInfo, err error) {
+	userInfo, errInfo := u.UserInfoByID(uid)
+
+	if err != nil {
+		userInfo.Msg = entity.CodeServerBusy.Msg()
+		userInfo.Code = entity.CodeServerBusy
+		return userInfo, errors.New(errInfo.Error())
+	}
+	existEmail, err := u.UserInfoByEmail(email)
+	if err != nil {
+		userInfo.Msg = entity.CodeServerBusy.Msg()
+		userInfo.Code = entity.CodeServerBusy
+		return userInfo, errors.New(errInfo.Error())
+	}
+	if existEmail.Uid > 0 {
+		userInfo.Msg = entity.CodeEmailExist.Msg()
+		userInfo.Code = entity.CodeEmailExist
+		userInfo.Body = *existEmail
+		return userInfo, entity.CodeEmailExist.Error()
+	}
+	var user entity.User
+	user.Uid = uid
+	user.Email = email
+
+	result := mysql.DBConn.Table(u.TableName()).Where("uid = ?", uid).UpdateColumns(&user)
+
+	if result.Error != nil {
+		return userInfo, result.Error
+	}
+
+	return userInfo, nil
+}

+ 52 - 3
service/user_poster.go

@@ -5,15 +5,22 @@ import (
 	"errors"
 	"icloudapp.cn/tools/entity"
 	"icloudapp.cn/tools/repository/mysql"
+	sEntity "icloudapp.cn/tools/service/entity"
+	"icloudapp.cn/tools/service/model"
 	"icloudapp.cn/tools/util"
 )
 
 type UserPoster struct {
-	ctx context.Context
+	Base
+	ctx   context.Context
+	query *model.MUserPoster
 }
 
-func NewPoster(ctx context.Context) *UserPoster {
-	return &UserPoster{ctx}
+func NewUserPoster(ctx context.Context) *UserPoster {
+	userPoster := &UserPoster{ctx: ctx}
+	userPoster.query = model.UserPoster
+
+	return userPoster
 }
 
 func (p *UserPoster) TableName() string {
@@ -58,3 +65,45 @@ func (p *UserPoster) InfoByUUID(uuid int64) (entity.UserPosterInfo, error) {
 	userPosterInfo.Body = userPoster
 	return userPosterInfo, nil
 }
+
+func (p *UserPoster) Count(uid int64) (count int64) {
+	if uid == 0 {
+		count, _ = p.query.WithContext(p.ctx).Count()
+	} else {
+		count, _ = p.query.WithContext(p.ctx).Where(p.query.UID.Eq(uid)).Count()
+	}
+	return count
+}
+
+func (p *UserPoster) Lists(uid int64, page int, pageSize int) (Page, []*sEntity.UserPoster) {
+	count := p.Count(uid)
+
+	pages := p.InitPages(count, page, pageSize)
+
+	if count == 0 {
+		return pages, nil
+	}
+	var lists []*sEntity.UserPoster
+	var err error
+	if uid > 0 {
+		lists, err = p.query.WithContext(p.ctx).Where(p.query.UID.Eq(uid)).Find()
+	} else {
+		lists, err = p.query.WithContext(p.ctx).Find()
+	}
+	if err != nil {
+		return pages, nil
+	}
+	return pages, lists
+}
+
+func (p *UserPoster) SimpleLists(uid int64) []*sEntity.UserPoster {
+	lists, _ := p.query.WithContext(
+		p.ctx).Select(
+		p.query.PosterID,
+		p.query.PosterName,
+		p.query.Preview,
+		p.query.Status,
+		p.query.Visits,
+		p.query.CreateAt).Where(p.query.UID.Eq(uid)).Find()
+	return lists
+}

BIN
util/go-test.png


+ 3 - 0
util/image/avatar.go

@@ -40,6 +40,9 @@ func (a *Avatar) SetAngle(angle float64) {
 	a.Angle = angle
 }
 func (a *Avatar) Create() (*imagick.MagickWand, error) {
+	if a.ViewSize.Width == 0 || a.ViewSize.Height == 0 {
+		return nil, util.NewError("Create Text", "指定元素未设置长宽")
+	}
 	border := imagick.NewDrawingWand()
 	border.SetStrokeOpacity(1)
 	border.SetStrokeColor(Pixel(a.BackgroundColor))

+ 4 - 1
util/image/image.go

@@ -25,6 +25,9 @@ func NewImage(path string) *Image {
 
 // Create 创建图像
 func (i *Image) Create() (*imagick.MagickWand, error) {
+	if i.ViewSize.Width == 0 || i.ViewSize.Height == 0 {
+		return nil, util.NewError("Create Text", "指定元素未设置长宽")
+	}
 	if i.File != "" {
 		i.File = fmt.Sprintf("%s/%s", i.Path, i.File)
 		if !fileObj.IsFile(i.File) {
@@ -58,7 +61,7 @@ func (i *Image) Create() (*imagick.MagickWand, error) {
 		if err := imageMW.RotateImage(OpacityPixel(), i.Angle); err != nil {
 			return nil, util.NewError(fmt.Sprintf("imageMW.RotateImage:%s", err.Error()))
 		}
-		fmt.Println(imageMW.GetImageWidth(), i.ViewSize.Width, imageMW.GetImageHeight(), i.ViewSize.Height)
+		fmt.Println("imageMW.GetImageWidth", imageMW.GetImageWidth(), i.ViewSize.Width, imageMW.GetImageHeight(), i.ViewSize.Height)
 		alignX := float64(imageMW.GetImageWidth()-i.ViewSize.Width) / 2
 		alignY := float64(imageMW.GetImageHeight()-i.ViewSize.Height) / 2
 		i.SetPoint(i.ViewPoint.X-alignX, i.ViewPoint.Y-alignY)

+ 11 - 4
util/image/poster.go

@@ -2,6 +2,7 @@ package image
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"gopkg.in/gographics/imagick.v3/imagick"
 	"icloudapp.cn/tools/util"
@@ -22,6 +23,7 @@ func NewPoster(ctx context.Context) *Poster {
 }
 
 func (p *Poster) Image(item Items) (*imagick.MagickWand, Items, error) {
+	util.TimeCost("Draw Image " + item.Value)()
 	var updated Items
 	image := NewImage(p.Path)
 	image.SetCache(p.CachePath)
@@ -36,6 +38,7 @@ func (p *Poster) Image(item Items) (*imagick.MagickWand, Items, error) {
 	return drawMw, updated, err
 }
 func (p *Poster) Text(item Items) (*imagick.MagickWand, Items, error) {
+	util.TimeCost("Draw Text " + item.Value)()
 	var updated Items
 	item.Value = strings.Replace(item.Value, "  ", " ", -1)
 	txt := NewText(item.Font, float64(item.Size), "")
@@ -61,6 +64,7 @@ func (p *Poster) Text(item Items) (*imagick.MagickWand, Items, error) {
 }
 
 func (p *Poster) QRCode(item Items) (*imagick.MagickWand, Items, error) {
+	util.TimeCost("QRCode Image " + item.Value)()
 	var updated Items
 	qr := NewQR()
 	qr.SetCache(p.CachePath)
@@ -80,6 +84,7 @@ func (p *Poster) QRCode(item Items) (*imagick.MagickWand, Items, error) {
 }
 
 func (p *Poster) Avatar(item Items) (*imagick.MagickWand, Items, error) {
+	util.TimeCost("Draw Avatar " + item.Value)()
 	var updated Items
 
 	avatar := NewAvatar()
@@ -104,6 +109,10 @@ type PosterMW struct {
 }
 
 func (p *Poster) Create(json PosterJson) (*imagick.MagickWand, error) {
+	defer util.TimeCost("创建海报")()
+	if json.Width == 0 || json.Height == 0 {
+		return nil, errors.New("create poster without width or height")
+	}
 	image := NewImage(util.Getwd())
 	image.SetCache(p.Path)
 	image.SetCache(p.CachePath)
@@ -125,7 +134,6 @@ func (p *Poster) Create(json PosterJson) (*imagick.MagickWand, error) {
 
 	itemLen := len(json.Items)
 	destChan := make(chan *PosterMW, itemLen)
-
 	defer close(destChan)
 
 	for index, item := range json.Items {
@@ -148,7 +156,6 @@ func (p *Poster) Create(json PosterJson) (*imagick.MagickWand, error) {
 			destChan <- &PosterMW{Index: index, Image: drawMW, Item: item, Err: drawErr}
 		}(item, index)
 	}
-
 	var response = make([]*PosterMW, itemLen)
 	for i := 0; i < len(json.Items); i++ {
 		res := <-destChan
@@ -158,11 +165,11 @@ func (p *Poster) Create(json PosterJson) (*imagick.MagickWand, error) {
 		draw := response[i].Image
 		errDraw := response[i].Err
 		if errDraw != nil {
-			fmt.Println("Response err", errDraw.Error())
+			fmt.Println("poster.errDraw : ", errDraw.Error())
 			continue
 		}
 		if errComposite := poster.CompositeImage(draw, imagick.COMPOSITE_OP_OVER, true, response[i].Item.X, response[i].Item.Y); errComposite != nil {
-			fmt.Println("poster.CompositeImage text err : ", errComposite.Error())
+			fmt.Println("poster.CompositeImage err : ", errComposite.Error())
 			continue
 		}
 	}

+ 4 - 1
util/image/qr.go

@@ -51,6 +51,9 @@ func (q *QR) SetAngle(angle float64) {
 	q.Angle = angle
 }
 func (q *QR) Create() (*imagick.MagickWand, error) {
+	if q.ViewSize.Width == 0 || q.ViewSize.Height == 0 {
+		return nil, util.NewError("Create qr", "未指定元素未设置长宽")
+	}
 	if q.BackgroundColor == "" {
 		q.BackgroundColor = "#000"
 	}
@@ -71,7 +74,7 @@ func (q *QR) Create() (*imagick.MagickWand, error) {
 		return nil, err
 	}
 
-	qr.DisableBorder = true
+	qr.DisableBorder = false
 	qr.ForegroundColor = frontColor
 	qr.BackgroundColor = backgroundColor
 

+ 3 - 1
util/image/text.go

@@ -146,6 +146,9 @@ func (t *Text) SetAngle(angle int64) {
 }
 
 func (t *Text) Create() (*imagick.MagickWand, error) {
+	if t.ViewSize.Width == 0 || t.ViewSize.Height == 0 {
+		return nil, util.NewError("Create Text", "指定元素未设置长宽")
+	}
 	txtDraw := imagick.NewDrawingWand()
 	if t.Font != "" {
 		if err := txtDraw.SetFont(fmt.Sprintf("upload/fonts/%s", t.Font)); err != nil {
@@ -206,7 +209,6 @@ func (t *Text) Create() (*imagick.MagickWand, error) {
 	} else {
 		mwBackground = Pixel(t.Background)
 	}
-
 	if err := subMw.NewImage(t.ViewSize.Width, t.ViewSize.Height, mwBackground); err != nil {
 		return nil, util.NewError("subMw.NewImage", err.Error())
 	}

+ 5 - 0
util/string/string.go

@@ -70,3 +70,8 @@ func ConvertInt64(s string) int64 {
 	}
 	return cInt
 }
+
+func ConvertANYInt64(s interface{}) int64 {
+
+	return 0
+}

+ 11 - 2
util/tools.go

@@ -98,7 +98,7 @@ func Encrypt(str string, key string) string {
 	keyByte = pkcs5Padding(keyByte, 16)
 	block, err := aes.NewCipher(keyByte)
 	if err != nil {
-		fmt.Println(err.Error())
+		fmt.Println("Encrypt err : ", err.Error())
 	}
 	blockSize := block.BlockSize() // 获取秘钥块的长度
 
@@ -117,7 +117,7 @@ func Decrypt(str string, key string) string {
 	keyByte = pkcs5Padding(keyByte, 16)
 	block, err := aes.NewCipher(keyByte) // 分组秘钥
 	if err != nil {
-		fmt.Println(err.Error())
+		fmt.Println("Decrypt err: ", err.Error())
 	}
 	blockSize := block.BlockSize()                                  // 获取秘钥块的长度
 	blockMode := cipher.NewCBCDecrypter(block, keyByte[:blockSize]) // 加密模式
@@ -189,3 +189,12 @@ func CutString(str string, start, end int) string {
 	}
 	return string(strRune[start:end])
 }
+
+// TimeCost 计算消耗的时间 defer TimeCost("xxx")()
+func TimeCost(title string) func() {
+	start := time.Now()
+	return func() {
+		tc := time.Since(start)
+		fmt.Printf("%s消耗%v\n", title, tc)
+	}
+}

+ 4 - 0
util/url/url.go

@@ -11,6 +11,10 @@ func ParseUrl(rawURL string) (*netUrl.URL, error) {
 	return netUrl.Parse(rawURL)
 }
 
+func ParseString(str string) (*netUrl.URL, error) {
+	return netUrl.ParseRequestURI(str)
+}
+
 // UrlEncode url-encode string
 // php urlencode
 func UrlEncode(str string) string {