feat(user):通过完成用户检索功能,继续确定项目的基本代码结构。
This commit is contained in:
parent
71f39c8c2f
commit
9aa32d99b9
9
cache/search.go
vendored
9
cache/search.go
vendored
|
@ -1,11 +1,16 @@
|
||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"electricity_bill_calc/logger"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var log = logger.Named("Cache")
|
||||||
|
|
||||||
func assembleSearchKey(entityName string, additional ...string) string {
|
func assembleSearchKey(entityName string, additional ...string) string {
|
||||||
var keys = make([]string, 0)
|
var keys = make([]string, 0)
|
||||||
keys = append(keys, strings.ToUpper(entityName))
|
keys = append(keys, strings.ToUpper(entityName))
|
||||||
|
@ -70,6 +75,10 @@ func RetrievePagedSearch[T any](entityName string, conditions ...string) (*T, in
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
|
if instance == nil || count == nil {
|
||||||
|
log.Warn("检索结果或者检索总数为空。", zap.String("searchKey", searchKey), zap.String("countKey", countKey))
|
||||||
|
return nil, -1, nil
|
||||||
|
}
|
||||||
return instance, count.Count, nil
|
return instance, count.Count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ type RedisSetting struct {
|
||||||
|
|
||||||
type ServiceSetting struct {
|
type ServiceSetting struct {
|
||||||
MaxSessionLife time.Duration
|
MaxSessionLife time.Duration
|
||||||
ItemsPageSize int
|
ItemsPageSize uint
|
||||||
CacheLifeTime time.Duration
|
CacheLifeTime time.Duration
|
||||||
HostSerial int64
|
HostSerial int64
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,12 @@ import (
|
||||||
"electricity_bill_calc/cache"
|
"electricity_bill_calc/cache"
|
||||||
"electricity_bill_calc/exceptions"
|
"electricity_bill_calc/exceptions"
|
||||||
"electricity_bill_calc/model"
|
"electricity_bill_calc/model"
|
||||||
|
"electricity_bill_calc/repository"
|
||||||
"electricity_bill_calc/response"
|
"electricity_bill_calc/response"
|
||||||
"electricity_bill_calc/security"
|
"electricity_bill_calc/security"
|
||||||
"electricity_bill_calc/service"
|
"electricity_bill_calc/service"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
@ -15,6 +17,7 @@ import (
|
||||||
func InitializeUserHandlers(router *fiber.App) {
|
func InitializeUserHandlers(router *fiber.App) {
|
||||||
router.Delete("/login", security.MustAuthenticated, doLogout)
|
router.Delete("/login", security.MustAuthenticated, doLogout)
|
||||||
router.Post("/login", doLogin)
|
router.Post("/login", doLogin)
|
||||||
|
router.Get("/account", security.ManagementAuthorize, searchUsers)
|
||||||
}
|
}
|
||||||
|
|
||||||
type _LoginForm struct {
|
type _LoginForm struct {
|
||||||
|
@ -53,13 +56,48 @@ func doLogin(c *fiber.Ctx) error {
|
||||||
|
|
||||||
func doLogout(c *fiber.Ctx) error {
|
func doLogout(c *fiber.Ctx) error {
|
||||||
result := response.NewResult(c)
|
result := response.NewResult(c)
|
||||||
session := c.Locals("session")
|
session, err := _retreiveSession(c)
|
||||||
if session == nil {
|
if err != nil {
|
||||||
return result.Success("用户会话已结束。")
|
return result.Success("用户会话已结束。")
|
||||||
}
|
}
|
||||||
_, err := cache.ClearSession(session.(*model.Session).Token)
|
_, err = cache.ClearSession(session.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result.Error(http.StatusInternalServerError, err.Error())
|
return result.Error(http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
return result.Success("用户已成功登出系统。")
|
return result.Success("用户已成功登出系统。")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func searchUsers(c *fiber.Ctx) error {
|
||||||
|
result := response.NewResult(c)
|
||||||
|
requestPage, err := strconv.Atoi(c.Query("page", "1"))
|
||||||
|
if err != nil {
|
||||||
|
return result.NotAccept("查询参数[page]格式不正确。")
|
||||||
|
}
|
||||||
|
requestKeyword := c.Query("keyword")
|
||||||
|
requestUserType, err := strconv.Atoi(c.Query("type", "-1"))
|
||||||
|
if err != nil {
|
||||||
|
return result.NotAccept("查询参数[type]格式不正确。")
|
||||||
|
}
|
||||||
|
var requestUserStat *bool
|
||||||
|
state, err := strconv.ParseBool(c.Query("state"))
|
||||||
|
if err != nil {
|
||||||
|
requestUserStat = nil
|
||||||
|
} else {
|
||||||
|
requestUserStat = &state
|
||||||
|
}
|
||||||
|
users, total, err := repository.UserRepository.FindUser(
|
||||||
|
&requestKeyword,
|
||||||
|
int16(requestUserType),
|
||||||
|
requestUserStat,
|
||||||
|
uint(requestPage),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return result.NotFound(err.Error())
|
||||||
|
}
|
||||||
|
return result.Json(
|
||||||
|
http.StatusOK,
|
||||||
|
"已取得符合条件的用户集合。",
|
||||||
|
response.NewPagedResponse(requestPage, total).ToMap(),
|
||||||
|
fiber.Map{"accounts": users},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"electricity_bill_calc/cache"
|
"electricity_bill_calc/cache"
|
||||||
|
"electricity_bill_calc/config"
|
||||||
"electricity_bill_calc/global"
|
"electricity_bill_calc/global"
|
||||||
"electricity_bill_calc/logger"
|
"electricity_bill_calc/logger"
|
||||||
"electricity_bill_calc/model"
|
"electricity_bill_calc/model"
|
||||||
|
@ -28,7 +29,7 @@ var UserRepository = _UserRepository{
|
||||||
// 使用用户名查询指定用户的基本信息
|
// 使用用户名查询指定用户的基本信息
|
||||||
func (ur _UserRepository) FindUserByUsername(username string) (*model.User, error) {
|
func (ur _UserRepository) FindUserByUsername(username string) (*model.User, error) {
|
||||||
ur.log.Info("根据用户名查询指定用户的基本信息。", zap.String("username", username))
|
ur.log.Info("根据用户名查询指定用户的基本信息。", zap.String("username", username))
|
||||||
if cachedUser, _ := cache.RetreiveEntity[model.User]("user", username); cachedUser != nil {
|
if cachedUser, _ := cache.RetrieveEntity[model.User]("user", username); cachedUser != nil {
|
||||||
ur.log.Info("已经从缓存获取到了符合指定用户名条件的用户基本信息。", zap.String("username", username))
|
ur.log.Info("已经从缓存获取到了符合指定用户名条件的用户基本信息。", zap.String("username", username))
|
||||||
return cachedUser, nil
|
return cachedUser, nil
|
||||||
}
|
}
|
||||||
|
@ -48,7 +49,7 @@ func (ur _UserRepository) FindUserByUsername(username string) (*model.User, erro
|
||||||
// 使用用户唯一编号查询指定用户的基本信息
|
// 使用用户唯一编号查询指定用户的基本信息
|
||||||
func (ur _UserRepository) FindUserById(uid string) (*model.User, error) {
|
func (ur _UserRepository) FindUserById(uid string) (*model.User, error) {
|
||||||
ur.log.Info("根据用户唯一编号查询指定用户的基本信息。", zap.String("user id", uid))
|
ur.log.Info("根据用户唯一编号查询指定用户的基本信息。", zap.String("user id", uid))
|
||||||
if cachedUser, _ := cache.RetreiveEntity[model.User]("user", uid); cachedUser != nil {
|
if cachedUser, _ := cache.RetrieveEntity[model.User]("user", uid); cachedUser != nil {
|
||||||
ur.log.Info("已经从缓存获取到了符合指定用户唯一编号的用户基本信息。")
|
ur.log.Info("已经从缓存获取到了符合指定用户唯一编号的用户基本信息。")
|
||||||
return cachedUser, nil
|
return cachedUser, nil
|
||||||
}
|
}
|
||||||
|
@ -68,7 +69,7 @@ func (ur _UserRepository) FindUserById(uid string) (*model.User, error) {
|
||||||
// 使用用户的唯一编号获取用户的详细信息
|
// 使用用户的唯一编号获取用户的详细信息
|
||||||
func (ur _UserRepository) FindUserDetailById(uid string) (*model.UserDetail, error) {
|
func (ur _UserRepository) FindUserDetailById(uid string) (*model.UserDetail, error) {
|
||||||
ur.log.Info("根据用户唯一编号查询指定用户的详细信息。", zap.String("user id", uid))
|
ur.log.Info("根据用户唯一编号查询指定用户的详细信息。", zap.String("user id", uid))
|
||||||
if cachedUser, _ := cache.RetreiveEntity[model.UserDetail]("user_detail", uid); cachedUser != nil {
|
if cachedUser, _ := cache.RetrieveEntity[model.UserDetail]("user_detail", uid); cachedUser != nil {
|
||||||
ur.log.Info("已经从缓存获取到了符合指定用户唯一编号的用户详细信息。")
|
ur.log.Info("已经从缓存获取到了符合指定用户唯一编号的用户详细信息。")
|
||||||
return cachedUser, nil
|
return cachedUser, nil
|
||||||
}
|
}
|
||||||
|
@ -88,7 +89,7 @@ func (ur _UserRepository) FindUserDetailById(uid string) (*model.UserDetail, err
|
||||||
// 使用用户唯一编号获取用户的综合详细信息
|
// 使用用户唯一编号获取用户的综合详细信息
|
||||||
func (ur _UserRepository) FindUserInformation(uid string) (*model.UserWithDetail, error) {
|
func (ur _UserRepository) FindUserInformation(uid string) (*model.UserWithDetail, error) {
|
||||||
ur.log.Info("根据用户唯一编号查询用户的综合详细信息", zap.String("user id", uid))
|
ur.log.Info("根据用户唯一编号查询用户的综合详细信息", zap.String("user id", uid))
|
||||||
if cachedUser, _ := cache.RetreiveEntity[model.UserWithDetail]("user_information", uid); cachedUser != nil {
|
if cachedUser, _ := cache.RetrieveEntity[model.UserWithDetail]("user_information", uid); cachedUser != nil {
|
||||||
ur.log.Info("已经从缓存获取到了符合指定用户唯一编号的用户综合详细信息。")
|
ur.log.Info("已经从缓存获取到了符合指定用户唯一编号的用户综合详细信息。")
|
||||||
return cachedUser, nil
|
return cachedUser, nil
|
||||||
}
|
}
|
||||||
|
@ -141,8 +142,31 @@ func (ur _UserRepository) IsUserExists(uid string) (bool, error) {
|
||||||
return userCount > 0, nil
|
return userCount > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查指定用户名在数据库中是否已经存在
|
||||||
|
func (ur _UserRepository) IsUsernameExists(username string) (bool, error) {
|
||||||
|
ur.log.Info("检查指定用户名在数据库中是否已经存在。", zap.String("username", username))
|
||||||
|
if exists, _ := cache.CheckExists("user", username); exists {
|
||||||
|
ur.log.Info("已经从缓存获取到了符合指定用户名的用户基本信息。")
|
||||||
|
return exists, nil
|
||||||
|
}
|
||||||
|
ctx, cancel := global.TimeoutContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var userCount int
|
||||||
|
sql, params, _ := ur.ds.From("user").Select(goqu.COUNT("*")).Where(goqu.Ex{"username": username}).Prepared(true).ToSQL()
|
||||||
|
if err := pgxscan.Get(ctx, global.DB, &userCount, sql, params...); err != nil {
|
||||||
|
ur.log.Error("从数据库查询指定用户名的用户基本信息失败。", zap.String("username", username), zap.Error(err))
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if userCount > 0 {
|
||||||
|
cache.CacheExists([]string{"user", fmt.Sprintf("user:%s", username)}, "user", username)
|
||||||
|
}
|
||||||
|
return userCount > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// 创建一个新用户
|
// 创建一个新用户
|
||||||
func (ur _UserRepository) CreateUser(user model.User, detail model.UserDetail, operator *string) (bool, error) {
|
func (ur _UserRepository) CreateUser(user model.User, detail model.UserDetail, operator *string) (bool, error) {
|
||||||
|
ur.log.Info("创建一个新用户。", zap.String("username", user.Username))
|
||||||
ctx, cancel := global.TimeoutContext()
|
ctx, cancel := global.TimeoutContext()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
tx, err := global.DB.Begin(ctx)
|
tx, err := global.DB.Begin(ctx)
|
||||||
|
@ -197,3 +221,106 @@ func (ur _UserRepository) CreateUser(user model.User, detail model.UserDetail, o
|
||||||
}
|
}
|
||||||
return userResult.RowsAffected() > 0 && detailResult.RowsAffected() > 0, nil
|
return userResult.RowsAffected() > 0 && detailResult.RowsAffected() > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据给定的条件检索用户
|
||||||
|
func (ur _UserRepository) FindUser(keyword *string, userType int16, state *bool, page uint) (*[]model.UserWithDetail, int64, error) {
|
||||||
|
ur.log.Info("根据给定的条件检索用户。", zap.Uint("page", page), zap.Stringp("keyword", keyword), zap.Int16("user type", userType), zap.Boolp("state", state))
|
||||||
|
if users, total, err := cache.RetrievePagedSearch[[]model.UserWithDetail]("user_with_detail", []string{
|
||||||
|
fmt.Sprintf("%d", page),
|
||||||
|
tools.CondFn(
|
||||||
|
func(v int16) bool {
|
||||||
|
return v != -1
|
||||||
|
},
|
||||||
|
userType,
|
||||||
|
fmt.Sprintf("%d", userType),
|
||||||
|
"UNDEF",
|
||||||
|
),
|
||||||
|
tools.DefaultStrTo("%s", state, "UNDEF"),
|
||||||
|
tools.DefaultTo(keyword, ""),
|
||||||
|
}...); err == nil && users != nil && total != -1 {
|
||||||
|
return users, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := global.TimeoutContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
userWithDetails []model.UserWithDetail
|
||||||
|
userCount int64
|
||||||
|
)
|
||||||
|
userQuery := ur.ds.
|
||||||
|
From(goqu.T("user").As("u")).
|
||||||
|
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.Ex{"ud.id": goqu.I("u.id")})).
|
||||||
|
Select(
|
||||||
|
"u.id", "u.username", "u.reset_needed", "u.type", "u.enabled",
|
||||||
|
"ud.name", "ud.abbr", "ud.region", "ud.address", "ud.contact", "ud.phone",
|
||||||
|
"ud.unit_service_fee", "ud.service_expiration",
|
||||||
|
"ud.created_at", "ud.created_by", "ud.last_modified_at", "ud.last_modified_by",
|
||||||
|
)
|
||||||
|
countQuery := ur.ds.
|
||||||
|
From(goqu.T("user").As("u")).
|
||||||
|
Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.Ex{"ud.id": goqu.I("u.id")})).
|
||||||
|
Select(goqu.COUNT("*"))
|
||||||
|
|
||||||
|
if keyword != nil && len(*keyword) > 0 {
|
||||||
|
pattern := fmt.Sprintf("%%%s%%", *keyword)
|
||||||
|
userQuery = userQuery.Where(
|
||||||
|
goqu.Or(
|
||||||
|
goqu.Ex{"u.username": goqu.Op{"like": pattern}},
|
||||||
|
goqu.Ex{"ud.name": goqu.Op{"like": pattern}},
|
||||||
|
goqu.Ex{"ud.abbr": goqu.Op{"like": pattern}},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
countQuery = countQuery.Where(
|
||||||
|
goqu.Or(
|
||||||
|
goqu.Ex{"u.username": goqu.Op{"like": pattern}},
|
||||||
|
goqu.Ex{"ud.name": goqu.Op{"like": pattern}},
|
||||||
|
goqu.Ex{"ud.abbr": goqu.Op{"like": pattern}},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if userType != -1 {
|
||||||
|
userQuery = userQuery.Where(goqu.Ex{"u.type": userType})
|
||||||
|
countQuery = countQuery.Where(goqu.Ex{"u.type": userType})
|
||||||
|
}
|
||||||
|
|
||||||
|
if state != nil {
|
||||||
|
userQuery = userQuery.Where(goqu.Ex{"u.enabled": state})
|
||||||
|
countQuery = countQuery.Where(goqu.Ex{"u.enabled": state})
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPosition := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||||
|
userQuery = userQuery.Offset(currentPosition).Limit(config.ServiceSettings.ItemsPageSize)
|
||||||
|
|
||||||
|
userSql, userParams, _ := userQuery.Prepared(true).ToSQL()
|
||||||
|
countSql, countParams, _ := countQuery.Prepared(true).ToSQL()
|
||||||
|
if err := pgxscan.Select(ctx, global.DB, &userWithDetails, userSql, userParams...); err != nil {
|
||||||
|
ur.log.Error("从数据库查询用户列表失败。", zap.Error(err))
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if err := pgxscan.Get(ctx, global.DB, &userCount, countSql, countParams...); err != nil {
|
||||||
|
ur.log.Error("从数据库查询用户列表总数失败。", zap.Error(err))
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
cache.CachePagedSearch(
|
||||||
|
userWithDetails,
|
||||||
|
userCount,
|
||||||
|
[]string{"user"},
|
||||||
|
"user_with_detail",
|
||||||
|
[]string{
|
||||||
|
fmt.Sprintf("%d", page),
|
||||||
|
tools.CondFn(
|
||||||
|
func(v int16) bool {
|
||||||
|
return v != -1
|
||||||
|
},
|
||||||
|
userType,
|
||||||
|
fmt.Sprintf("%d", userType),
|
||||||
|
"UNDEF",
|
||||||
|
),
|
||||||
|
tools.DefaultStrTo("%s", state, "UNDEF"),
|
||||||
|
tools.DefaultTo(keyword, ""),
|
||||||
|
}...,
|
||||||
|
)
|
||||||
|
return &userWithDetails, userCount, nil
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ type BaseResponse struct {
|
||||||
|
|
||||||
type PagedResponse struct {
|
type PagedResponse struct {
|
||||||
Page int `json:"current"`
|
Page int `json:"current"`
|
||||||
Size int `json:"pageSize"`
|
Size uint `json:"pageSize"`
|
||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func SessionRecovery(c *fiber.Ctx) error {
|
func SessionRecovery(c *fiber.Ctx) error {
|
||||||
if auth := c.Get("Authorization", ""); len(auth) > 0 {
|
if auth := c.Get("Authorization", ""); len(auth) > 0 {
|
||||||
token := strings.Fields(auth)[1]
|
token := strings.Fields(auth)[1]
|
||||||
session, err := cache.RetreiveSession(token)
|
session, err := cache.RetrieveSession(token)
|
||||||
|
|
||||||
if err == nil && session != nil {
|
if err == nil && session != nil {
|
||||||
c.Locals("session", session)
|
c.Locals("session", session)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package tools
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mozillazg/go-pinyin"
|
"github.com/mozillazg/go-pinyin"
|
||||||
|
@ -51,3 +52,37 @@ func PartitionSlice[T any](slice []T, chunkSize int) [][]T {
|
||||||
}
|
}
|
||||||
return divided
|
return divided
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断指定指针是否为空,如果为空,则返回指定默认值(指针形式)
|
||||||
|
func DefaultTo[T any](originValue *T, defaultValue T) T {
|
||||||
|
if originValue == nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return *originValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断指定的指针是否为空,如果为空,则返回指定的默认字符串,或者返回指针所指内容的字符串形式。
|
||||||
|
func DefaultStrTo[T any](format string, originValue *T, defaultStr string) string {
|
||||||
|
if originValue == nil {
|
||||||
|
return defaultStr
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(format, originValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断指定表达式的值,根据表达式的值返回指定的值。相当于其他语言中的三目运算符。
|
||||||
|
func Cond[T any](expr bool, trueValue, falseValue T) T {
|
||||||
|
if expr {
|
||||||
|
return trueValue
|
||||||
|
}
|
||||||
|
return falseValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用给定的函数对指定的值进行判断,根据表达式的值返回指定的值。
|
||||||
|
func CondFn[T, R any](exprFn func(val T) bool, value T, trueValue, falseValue R) R {
|
||||||
|
return Cond(exprFn(value), trueValue, falseValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用给定的函数对指定的值进行判断,如果表达式为真,则返回指定的值,否则返回另一个值。
|
||||||
|
func CondOr[T any](exprFn func(val T) bool, value, elseValue T) T {
|
||||||
|
return CondFn(exprFn, value, value, elseValue)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user