商品价格计划
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pricing/app/rts.go

525 lines
11 KiB

package app
import (
"errors"
"github.com/go-chi/chi/v5"
"github.com/go-chi/jwtauth/v5"
"gorm.io/gorm"
"net/http"
"strings"
)
var tokenAuth *jwtauth.JWTAuth
func init() {
tokenAuth = jwtauth.New("HS256", []byte("secret"), nil)
}
type userInfo struct {
id uint
Name string
PhoneNumber string
Password string
}
func (u *userInfo) Bind(r *Request) error {
if name, ok := r.Get("name"); ok && len(name) > 0 {
u.Name = name
} else {
return NewError(1, "缺少用户名称")
}
if phoneNumber, ok := r.Get("phone_number"); ok && len(phoneNumber) > 0 {
if len(phoneNumber) != 11 {
return NewError(2, "手机号码格式错误")
}
u.PhoneNumber = phoneNumber
} else {
return NewError(2, "缺少手机号码")
}
if password, ok := r.Get("password"); ok && len(password) > 0 {
if len(password) < 6 {
return NewError(2, "密码太短")
}
u.Password = password
} else {
return NewError(2, "缺少密码")
}
return nil
}
// CreateUser 创建用户
func CreateUser(w *ResponseWriter, r *Request) {
var ui userInfo
if err := ui.Bind(r); err != nil {
w.Error(err)
return
}
var count int64
if err := DB.Model(&User{}).Where("phone_number = ?", ui.PhoneNumber).Count(&count).Error; err != nil {
w.Error(err)
return
}
if count > 0 {
w.Error(NewError(2, "手机号码已经被使用了"))
return
}
hash, err := HashPassword(ui.Password)
if err != nil {
LogError(err)
w.Error(NewError(1, "加密密码失败"))
return
}
user := User{
Name: ui.Name,
PhoneNumber: ui.PhoneNumber,
Password: hash,
Admin: false,
}
if err := DB.Create(&user).Error; err != nil {
w.Error(err)
} else {
w.Ok(ui, "创建用户成功")
}
}
// UpdateUser 更新用户
func UpdateUser(w *ResponseWriter, r *Request) {
id := chi.URLParam(r.Request, "id")
if len(id) == 0 {
w.Error(NewError(1, "缺少用户ID"))
}
var ui userInfo
if err := ui.Bind(r); err != nil {
w.Error(err)
return
}
// 查询用户信息
var user User
if err := DB.First(&user, "id = ?", id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
err = NewError(1, "用户不存在")
}
w.Error(err)
return
}
// 用户信息未发生变化
if ui.Name == user.Name && ui.PhoneNumber == user.PhoneNumber && CheckPasswordHash(ui.Password, user.Password) {
w.Ok(nil, "操作成功")
return
}
// 检查手机号码是否被使用
var count int64
if err := DB.Model(&User{}).Where("phone_number = ? AND id != ?", ui.PhoneNumber, id).Count(&count).Error; err != nil {
w.Error(err)
return
}
if count > 0 {
w.Error(NewError(1, "手机号码已被使用"))
return
}
// 保存用户信息
user.Name = ui.Name
user.PhoneNumber = ui.PhoneNumber
user.Password = ui.Password
if err := DB.Save(&user).Error; err != nil {
w.Error(err)
} else {
w.Ok(nil, "操作成功")
}
}
// DeleteUser 删除用户
func DeleteUser(w *ResponseWriter, r *Request) {
id := chi.URLParam(r.Request, "id")
if len(id) == 0 {
w.Error(NewError(1, "缺少用户ID"))
}
// 查询用户信息
var user User
if err := DB.First(&user, "id = ?", id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
err = NewError(1, "用户不存在")
}
w.Error(err)
return
}
// 删除用户
if err := DB.Delete(&user).Error; err != nil {
LogError(err)
w.Error(NewError(1, "删除用户失败"))
} else {
w.Ok(nil, "删除用户成功")
}
}
// ListUser 用户列表
func ListUser(w *ResponseWriter, r *Request) {
search := func(db *gorm.DB) *gorm.DB {
return db.
Model(&User{}).
Scopes(Search(r, "name", "name LIKE ?")).
Scopes(Paginate(r))
}
var userList []User
var total int64
var err error
if err = DB.Scopes(search).Count(&total).Error; err == nil {
err = DB.Scopes(search).Find(&userList).Error
}
if err != nil {
w.Error(err)
} else {
w.Ok(map[string]any{
"list": userList,
"total": total,
})
}
}
// Login 用户登录
func Login(w *ResponseWriter, r *Request) {
var phoneNumber string
var password string
var ok bool
// 提交的手机号码
if phoneNumber, ok = r.Get("phone_number"); ok && len(phoneNumber) > 0 {
if len(phoneNumber) != 11 {
w.Error(NewError(2, "手机号码格式错误"))
return
}
} else {
w.Error(NewError(2, "缺少手机号码"))
return
}
// 提交的登陆密码
if password, ok = r.Get("password"); ok && len(password) > 0 {
if len(password) < 6 {
w.Error(NewError(2, "密码太短"))
return
}
} else {
w.Error(NewError(2, "缺少密码"))
return
}
// 查询用户是否存在
var user User
if err := DB.First(&user, "phone_number = ?", phoneNumber).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
err = NewError(1, "手机号码或密码错误")
}
w.Error(err)
return
}
// 验证密码
if !CheckPasswordHash(password, user.Password) {
w.Error(NewError(1, "手机号码或密码错误"))
return
}
ut, err := GenerateAuthToken(r, user.ID)
if err == nil {
err = DB.Create(&ut).Error
}
if err != nil {
w.Error(err)
return
}
w.Ok(map[string]any{
"user": user,
"access_token": ut.AccessToken,
"refresh_token": ut.RefreshToken,
}, "登录成功")
}
// RefreshToken 刷新授权令牌
func RefreshToken(w *ResponseWriter, r *Request) {
// 获取刷新令牌信息
ut, err := AuthInfo(r)
if err != nil {
w.Error(err)
return
}
// 生成新的令牌
ut2, err := GenerateAuthToken(r, ut.UserID)
if err != nil {
w.Error(err)
return
}
// 删除旧的令牌,保持新的令牌
err = DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Delete(&ut).Error; err != nil {
return err
}
return tx.Create(&ut2).Error
})
if err != nil {
w.Error(err)
return
}
w.Ok(map[string]any{
"access_token": ut.AccessToken,
"refresh_token": ut.RefreshToken,
}, "刷新令牌成功")
}
// CreateGoods 创建商品
func CreateGoods(w *ResponseWriter, r *Request) {
name := r.Value("name")
price := r.Float32("price", 0)
if len(name) == 0 {
w.Error(NewError(1, "商品名称错误"))
return
}
if price <= 0 {
w.Error(NewError(2, "商品价格错误"))
return
}
// 加载登录用户信息
ut, ok := UserTokenFromContext(r.Context())
if !ok {
w.Error(NewError(1, "找不到登录用户消息"))
return
}
// 防止商品重名
var goods Goods
err := DB.First(&goods, "name = ?", name).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
goods = Goods{
Name: name,
Price: price,
CreatedBy: ut.UserID,
Creator: ut.User,
UpdatedBy: ut.UserID,
Updater: ut.User,
}
err = DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&goods).Error; err != nil {
return err
}
return tx.Create(&Price{
GoodsID: goods.ID,
Price: goods.Price,
CreatedBy: goods.CreatedBy,
CreatedAt: goods.CreatedAt,
}).Error
})
if err != nil {
w.Error(NewError(3, "创建商品失败"))
} else {
w.Ok(goods)
}
} else if err != nil {
w.Error(err)
} else {
w.Error(NewError(5, "商品已经存在"))
}
}
// UpdateGoods 更新产品信息
func UpdateGoods(w *ResponseWriter, r *Request) {
name := r.Value("name")
price := r.Float32("price", 0)
id := uint(r.Uint64("id", 0))
if len(name) == 0 {
w.Error(NewError(1, "商品名称错误"))
return
}
if price <= 0 {
w.Error(NewError(2, "商品价格错误"))
return
}
// 加载登录用户信息
ut, ok := UserTokenFromContext(r.Context())
if !ok {
w.Error(NewError(1, "找不到登录用户消息"))
return
}
// 查询商品
var goods Goods
err := DB.First(&goods, "id = ?", id).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
w.Error(NewError(2, "商品不存在"))
return
} else if err != nil {
w.Error(err)
return
}
// 商品名称不能重复
err = DB.Where("id <> ?", id).First(&Goods{}, "name = ?", name).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
w.Error(err)
return
}
// 信息未改变
if goods.Name == name && goods.Price == price {
w.Error(NewError(2, "数据未变化"))
return
}
// 价格未改变
if goods.Price == price {
if goods.Name != name {
goods.Name = name
goods.UpdatedBy = ut.UserID
goods.Updater = ut.User
err = DB.Save(&goods).Error
if err != nil {
w.Error(err)
return
}
}
w.Ok(goods, "修改成功")
return
}
// 修改商品信息并记录价格变化
err = DB.Transaction(func(tx *gorm.DB) error {
goods.Name = name
goods.Price = price
goods.UpdatedBy = ut.UserID
goods.Updater = ut.User
if err = tx.Save(&goods).Error; err != nil {
return err
}
// 记录价格变化
return tx.Create(&Price{
GoodsID: goods.ID,
Price: goods.Price,
CreatedBy: ut.UserID,
Creator: ut.User,
}).Error
})
if err != nil {
w.Error(err)
} else {
w.Ok(goods, "修改成功")
}
}
// ListGoods 查询商品列表
func ListGoods(w *ResponseWriter, r *Request) {
search := func(db *gorm.DB) *gorm.DB {
return db.
Model(&Goods{}).
Scopes(TimeRange(r, "created_at")).
Scopes(Paginate(r)).
Scopes(Search(r, "name", "name LIKE ?"))
}
var goodsList []Goods
var total int64
var err error
if err = DB.Scopes(search).Count(&total).Error; err == nil {
err = DB.Scopes(search).Preload("Creator").Preload("Updater").Find(&goodsList).Error
}
if err != nil {
w.Error(err)
} else {
w.Ok(map[string]any{
"list": goodsList,
"total": total,
})
}
}
// 查询价格列表
func ListPrices(w *ResponseWriter, r *Request) {
id := uint(r.Uint64("id", 0))
var goods Goods
if err := DB.Scopes(func(db *gorm.DB) *gorm.DB {
var queries []string
var args []any
if val, ok := r.Get("start_time"); ok {
queries = append(queries, "created_at >= ?")
args = append(args, val)
}
if val, ok := r.Get("end_time"); ok {
queries = append(queries, "created_at <= ?")
args = append(args, val)
}
if len(queries) == 0 {
return db.Preload("Prices.Creator")
}
args = append([]any{strings.Join(queries, " AND ")}, args...)
return db.Preload("Prices", args...)
}).First(&goods, "id = ?", id).Error; err != nil {
w.Error(err)
} else {
w.Ok(goods)
}
}
// RegisterRoutes 注册路由
func RegisterRoutes(r chi.Router) {
// 登录接口
r.Post("/login", Handler(Login))
// 需要登录权限的
r.Group(func(r chi.Router) {
r.Use(jwtauth.Verifier(tokenAuth))
r.Use(jwtauth.Authenticator)
r.Use(CheckAuthToken)
// 刷新令牌
r.Get("/refresh-token", Handler(RefreshToken))
// 管理员
r.Group(func(r chi.Router) {
// 验证是不是管理员
r.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ut, ok := r.Context().Value("USER_TOKEN").(*UserToken)
if ok && ut.User != nil && ut.User.Admin {
next.ServeHTTP(w, r)
return
}
NewResponseWriter(w).Error(&Error{
Status: http.StatusInternalServerError,
Code: 2,
Message: "用户信息错误",
})
})
})
r.Post("/user", Handler(CreateUser))
r.Patch("/user/:id", Handler(UpdateUser))
r.Delete("/user/:id", Handler(DeleteUser))
r.Get("/users", Handler(ListUser))
})
// 普通用户
r.Group(func(r chi.Router) {
r.Post("/goods", Handler(CreateGoods))
r.Patch("/goods/:id", Handler(UpdateGoods))
r.Get("/goods", Handler(ListGoods))
r.Get("/goods/:id/prices", Handler(ListPrices))
})
})
}