调整微信扫码登录相关接口,拆分门店奖励:奖励类型、奖励详情

This commit is contained in:
2025-06-03 11:06:00 +08:00
parent ea87bc829e
commit fdc9cc3463
37 changed files with 698 additions and 189 deletions

View File

@ -5,7 +5,9 @@ import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcmd"
"server/internal/controller/admin"
"server/internal/controller/auth"
"server/internal/controller/wx"
"server/internal/middleware"
)
@ -21,7 +23,15 @@ var (
group.Middleware(ghttp.MiddlewareCORS)
group.Bind(
auth.NewV1(),
wx.NewV1(),
)
group.Group("/x", func(group *ghttp.RouterGroup) {
group.Middleware(middleware.Auth)
group.Middleware(middleware.Casbin)
group.Bind(
admin.NewV1(),
)
})
})
s.Run()
return nil

View File

@ -1 +1,13 @@
package consts
const (
GuestPermission = "guest"
UserPermission = "user"
AdminPermission = "admin"
MerchantPermission = "merchant"
StorePermission = "store"
)
const (
QRCodeExpireTime = 60
QRCodeLimitTime = 10
)

View File

@ -2,13 +2,19 @@ package admin
import (
"context"
"server/internal/model"
"server/internal/service"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"server/api/admin/v1"
v1 "server/api/admin/v1"
)
func (c *ControllerV1) AdminInfo(ctx context.Context, req *v1.AdminInfoReq) (res *v1.AdminInfoRes, err error) {
return nil, gerror.NewCode(gcode.CodeNotImplemented)
userId := g.RequestFromCtx(ctx).GetCtxVar("userId").Int()
out, err := service.Admin().Info(ctx, &model.AdminInfoIn{Id: userId})
if err != nil {
return nil, err
}
return &v1.AdminInfoRes{Username: out.Username}, nil
}

View File

@ -2,13 +2,19 @@ package auth
import (
"context"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"server/api/auth/v1"
v1 "server/api/auth/v1"
"server/internal/model"
"server/internal/service"
)
func (c *ControllerV1) AdminLogin(ctx context.Context, req *v1.AdminLoginReq) (res *v1.AdminLoginRes, err error) {
return nil, gerror.NewCode(gcode.CodeNotImplemented)
out, err := service.Admin().Login(ctx, &model.AdminLoginIn{
Username: req.Username,
Password: req.Password,
})
if err != nil {
return nil, err
}
return &v1.AdminLoginRes{Token: out.Token}, nil
}

View File

@ -2,32 +2,90 @@ package wx
import (
"context"
"github.com/gogf/gf/v2/os/glog"
"fmt"
v1 "server/api/wx/v1"
"server/internal/model"
"server/internal/service"
"strings"
"server/api/auth/v1"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/glog"
)
func (c *ControllerV1) WeChatEvent(ctx context.Context, req *v1.WeChatEventReq) (res *v1.WeChatEventRes, err error) {
// 收到微信订阅事件
glog.Infof(ctx,
"微信消息推送:时间=%d, 消息类型=%s, 事件=%s, 事件Key=%s",
req.CreateTime,
req.MsgType,
req.Event,
req.EventKey,
glog.Infof(ctx, "【微信事件】收到事件 | event=%s, msgType=%s, eventKey=%s, fromUserName=%s, toUserName=%s, createTime=%d",
req.Event, req.MsgType, req.EventKey, req.FromUserName, req.ToUserName, req.CreateTime,
)
// 根据事件类型进行不同的处理:
switch req.MsgType {
case "event":
switch req.Event {
case "subscribe":
// 未关注,扫描关注后, 注册账号,关联微信的 open_id
key := strings.TrimPrefix(req.EventKey, "qrscene_")
out, err := service.User().Login(ctx, &model.UserLoginIn{OpenId: req.FromUserName})
if err != nil {
return nil, err
}
if err = updateLoginCache(ctx, key, out.Token); err != nil {
glog.Errorf(ctx, "【微信事件】更新登录缓存失败 | error=%s", err.Error())
}
return nil, nil
case "SCAN":
// 已关注,扫描后,根据 open_id 查找用户生成 token
out, err := service.User().Login(ctx, &model.UserLoginIn{OpenId: req.FromUserName})
if err != nil {
return nil, err
}
if err = updateLoginCache(ctx, req.EventKey, out.Token); err != nil {
glog.Errorf(ctx, "【微信事件】更新登录缓存失败 | error=%s", err.Error())
}
return nil, nil
default:
// 处理其他事件
glog.Infof(ctx, "【微信事件】不支持的事件 | event=%s", req.Event)
return nil, nil
}
default:
glog.Infof(ctx, "【微信事件】不支持的消息类型 | msgType=%s", req.MsgType)
return nil, nil
}
return nil, nil
}
func updateLoginCache(ctx context.Context, key string, token string) error {
loginCacheKey := fmt.Sprintf("wx:login:cache:%s", key)
glog.Infof(ctx, "【微信事件】准备更新登录缓存 | redisKey=%s, token=%s", loginCacheKey, token)
// 获取原缓存
data, err := g.Redis().Get(ctx, loginCacheKey)
if err != nil {
glog.Errorf(ctx, "【微信事件】获取缓存失败 | key=%s, error=%v", loginCacheKey, err)
return err
}
if data.IsEmpty() {
glog.Warningf(ctx, "【微信事件】缓存不存在 | key=%s", loginCacheKey)
return nil // 不是错误,只是二维码超时或错误
}
// 反序列化
var loginCache model.LoginCache
if err := gjson.Unmarshal(data.Bytes(), &loginCache); err != nil {
glog.Errorf(ctx, "【微信事件】反序列化缓存失败 | key=%s, error=%v", loginCacheKey, err)
return err
}
// 更新状态与Token
loginCache.Status = 1
loginCache.Token = token
// 序列化并写回 Redis
newData, err := gjson.Marshal(loginCache)
if err != nil {
glog.Errorf(ctx, "【微信事件】序列化缓存失败 | key=%s, error=%v", loginCacheKey, err)
return err
}
if err := g.Redis().SetEX(ctx, loginCacheKey, newData, 60); err != nil {
glog.Errorf(ctx, "【微信事件】写入缓存失败 | key=%s, error=%v", loginCacheKey, err)
return err
}
glog.Infof(ctx, "【微信事件】缓存更新成功 | key=%s, token=%s", loginCacheKey, token)
return nil
}

View File

@ -2,14 +2,54 @@ package wx
import (
"context"
"encoding/json"
"fmt"
v1 "server/api/wx/v1"
"server/internal/model"
"server/utility/ecode"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"server/api/auth/v1"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/glog"
)
func (c *ControllerV1) WeChatPolling(ctx context.Context, req *v1.WeChatPollingReq) (res *v1.WeChatPollingRes, err error) {
// 收到请求根据 uuid 查询缓存中的数据,看看是否生成了 token
return nil, gerror.NewCode(gcode.CodeNotImplemented)
loginCacheKey := fmt.Sprintf("wx:login:cache:%s", req.SceneId)
glog.Infof(ctx, "开始处理微信长轮询请求SceneID: %s", req.SceneId)
var loginCache model.LoginCache
data, err := g.Redis().Get(ctx, loginCacheKey)
if err != nil {
glog.Errorf(ctx, "从 Redis 获取登录缓存失败SceneID: %s错误: %v", req.SceneId, err)
return nil, ecode.Fail.Sub("获取登录状态失败")
}
if data.IsEmpty() {
glog.Warningf(ctx, "用户尚未扫码登录SceneID: %s", req.SceneId)
return nil, ecode.InvalidOperation.Sub("请先调用获取二维码登录")
}
if err = json.Unmarshal(data.Bytes(), &loginCache); err != nil {
glog.Errorf(ctx, "解析登录状态失败SceneID: %s错误: %v", req.SceneId, err)
return nil, ecode.Fail.Sub("解析登录状态失败")
}
switch loginCache.Status {
case 0:
glog.Infof(ctx, "用户尚未扫码登录SceneID: %s", req.SceneId)
return &v1.WeChatPollingRes{
Status: "waiting",
Token: "",
}, nil
case 1:
// 直接返回缓存的token
glog.Infof(ctx, "用户扫码登录成功SceneID: %s返回缓存Token", req.SceneId)
return &v1.WeChatPollingRes{
Status: "success",
Token: loginCache.Token,
}, nil
default:
glog.Warningf(ctx, "未知登录状态 %dSceneID: %s", loginCache.Status, req.SceneId)
return nil, ecode.InvalidOperation.Sub("未知登录状态")
}
}

View File

@ -2,31 +2,34 @@ package wx
import (
"context"
"github.com/gogf/gf/v2/crypto/gsha1"
"crypto/sha1"
"fmt"
"github.com/gogf/gf/v2/frame/g"
"server/utility/ecode"
"server/utility/wechat"
"sort"
"strings"
"server/api/auth/v1"
"server/api/wx/v1"
)
func (c *ControllerV1) WeChatVertify(ctx context.Context, req *v1.WeChatVertifyReq) (res *v1.WeChatVertifyRes, err error) {
weChatToken := g.Config().MustGet(ctx, "wechat.token").String()
// 1. 将 token、timestamp、nonce 组成 slice
params := []string{weChatToken, req.Timestamp, req.Nonce}
// 2. 字典序排序
// 1. 排序
params := []string{wechat.GetWeChatClient().GetToken(), req.Timestamp, req.Nonce}
sort.Strings(params)
// 3. 拼接字符串
joined := strings.Join(params, "")
// 4. SHA1 加密
encrypt := gsha1.Encrypt(joined)
// 5. 与 signature 对比
if encrypt != req.Signature {
return nil, ecode.InvalidOperation.Sub("微信服务器验证失败")
// 2. 拼接成字符串
str := strings.Join(params, "")
// 3. SHA1 加密
h := sha1.New()
h.Write([]byte(str))
sha1Str := fmt.Sprintf("%x", h.Sum(nil))
// 4. 比较签名
if sha1Str != req.Signature {
return nil, fmt.Errorf("签名错误")
}
g.RequestFromCtx(ctx).Response.WriteJson(req.EchoStr)
return
g.RequestFromCtx(ctx).Response.Write(req.EchoStr)
return nil, nil
}

View File

@ -26,10 +26,7 @@ type AdminsColumns struct {
RealName string // 真实姓名
Phone string // 手机号
Email string // 邮箱
Role string // 角色1=超级管理员2=运营管理员3=客服管理员4=财务管理员
Status string // 状态1=正常2=禁用
LastLoginAt string // 最后登录时间
LastLoginIp string // 最后登录IP
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
DeletedAt string // 软删除时间戳
@ -43,10 +40,7 @@ var adminsColumns = AdminsColumns{
RealName: "real_name",
Phone: "phone",
Email: "email",
Role: "role",
Status: "status",
LastLoginAt: "last_login_at",
LastLoginIp: "last_login_ip",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
DeletedAt: "deleted_at",

View File

@ -0,0 +1,87 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// RewardTypesDao is the data access object for the table reward_types.
type RewardTypesDao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of the current DAO.
columns RewardTypesColumns // columns contains all the column names of Table for convenient usage.
}
// RewardTypesColumns defines and stores column names for the table reward_types.
type RewardTypesColumns struct {
Id string // 类型ID
Name string // 类型名称
Code string // 类型编码
Description string // 类型描述
Status string // 状态1=启用2=禁用
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
DeletedAt string // 软删除时间
}
// rewardTypesColumns holds the columns for the table reward_types.
var rewardTypesColumns = RewardTypesColumns{
Id: "id",
Name: "name",
Code: "code",
Description: "description",
Status: "status",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
DeletedAt: "deleted_at",
}
// NewRewardTypesDao creates and returns a new DAO object for table data access.
func NewRewardTypesDao() *RewardTypesDao {
return &RewardTypesDao{
group: "default",
table: "reward_types",
columns: rewardTypesColumns,
}
}
// DB retrieves and returns the underlying raw database management object of the current DAO.
func (dao *RewardTypesDao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of the current DAO.
func (dao *RewardTypesDao) Table() string {
return dao.table
}
// Columns returns all column names of the current DAO.
func (dao *RewardTypesDao) Columns() RewardTypesColumns {
return dao.columns
}
// Group returns the database configuration group name of the current DAO.
func (dao *RewardTypesDao) Group() string {
return dao.group
}
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
func (dao *RewardTypesDao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rolls back the transaction and returns the error if function f returns a non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note: Do not commit or roll back the transaction in function f,
// as it is automatically handled by this function.
func (dao *RewardTypesDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@ -20,30 +20,30 @@ type StoreRewardsDao struct {
// StoreRewardsColumns defines and stores column names for the table store_rewards.
type StoreRewardsColumns struct {
Id string // 门店奖励ID
StoreId string // 所属门店ID
RewardType string // 奖励类型1=积分2=优惠券3=商品4=抽奖券
RewardName string // 奖励名称
Amount string // 奖励数量
Total string // 该奖励总库存NULL 表示无限)
MerchantId string // 所属商户ID
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
DeletedAt string // 软删除时间戳
Id string // 门店奖励ID
StoreId string // 所属门店ID
RewardTypeId string // 奖励类型ID
RewardName string // 奖励名称
Amount string // 奖励数量
Total string // 该奖励总库存NULL 表示无限)
MerchantId string // 所属商户ID
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
DeletedAt string // 软删除时间戳
}
// storeRewardsColumns holds the columns for the table store_rewards.
var storeRewardsColumns = StoreRewardsColumns{
Id: "id",
StoreId: "store_id",
RewardType: "reward_type",
RewardName: "reward_name",
Amount: "amount",
Total: "total",
MerchantId: "merchant_id",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
DeletedAt: "deleted_at",
Id: "id",
StoreId: "store_id",
RewardTypeId: "reward_type_id",
RewardName: "reward_name",
Amount: "amount",
Total: "total",
MerchantId: "merchant_id",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
DeletedAt: "deleted_at",
}
// NewStoreRewardsDao creates and returns a new DAO object for table data access.

View File

@ -23,10 +23,8 @@ type UserLoginRecordsColumns struct {
Id string // 记录ID
UserId string // 用户ID
StoreId string // 登录门店ID
MerchantId string // 所属商户ID
LoginIp string // 登录IP地址
LoginDevice string // 登录设备信息
LoginPlatform string // 登录平台1=Web2=iOS3=Android4=微信小程序5=支付宝小程序6=其他
LoginPlatform string // 登录平台1=PC
LoginType string // 登录方式1=微信2=手机号3=账号密码4=其他
LoginStatus string // 登录状态1=成功2=失败
FailReason string // 失败原因
@ -40,9 +38,7 @@ var userLoginRecordsColumns = UserLoginRecordsColumns{
Id: "id",
UserId: "user_id",
StoreId: "store_id",
MerchantId: "merchant_id",
LoginIp: "login_ip",
LoginDevice: "login_device",
LoginPlatform: "login_platform",
LoginType: "login_type",
LoginStatus: "login_status",

View File

@ -0,0 +1,27 @@
// =================================================================================
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package dao
import (
"server/internal/dao/internal"
)
// internalRewardTypesDao is an internal type for wrapping the internal DAO implementation.
type internalRewardTypesDao = *internal.RewardTypesDao
// rewardTypesDao is the data access object for the table reward_types.
// You can define custom methods on it to extend its functionality as needed.
type rewardTypesDao struct {
internalRewardTypesDao
}
var (
// RewardTypes is a globally accessible object for table reward_types operations.
RewardTypes = rewardTypesDao{
internal.NewRewardTypesDao(),
}
)
// Add your custom methods and functionality below.

View File

@ -2,6 +2,8 @@ package admin
import (
"context"
"github.com/gogf/gf/v2/os/glog"
"server/internal/consts"
"server/internal/dao"
"server/internal/model"
"server/internal/model/do"
@ -19,8 +21,29 @@ func New() service.IAdmin {
return &sAdmin{}
}
func checkAdmin() {
ctx := context.Background()
exist, err := dao.Admins.Ctx(ctx).Where(do.Admins{Username: "admin"}).Exist()
if err != nil {
panic("初始化管理员失败")
}
if !exist {
passwordHash, _ := utility.EncryptPassword("Aa123456")
_, err = dao.Admins.Ctx(ctx).Insert(do.Admins{
Username: "admin",
PasswordHash: passwordHash,
Status: 1,
})
if err != nil {
panic("初始化管理员失败")
}
}
glog.Infof(ctx, "初始化管理员成功")
}
func init() {
service.RegisterAdmin(New())
go checkAdmin()
}
func (s *sAdmin) Login(ctx context.Context, in *model.AdminLoginIn) (out *model.LoginOut, err error) {
exist, err := dao.Admins.Ctx(ctx).Exist(do.Admins{Username: in.Username})
@ -35,12 +58,13 @@ func (s *sAdmin) Login(ctx context.Context, in *model.AdminLoginIn) (out *model.
return nil, ecode.Fail.Sub("查询管理员失败")
}
if !utility.ComparePassword(admin.PasswordHash, in.Password) {
return nil, ecode.Auth.Sub("密码错误")
return nil, ecode.Auth
}
token, err := jwt.GenerateToken(&jwt.TokenIn{UserId: admin.Id, Permission: "admin"})
token, err := jwt.GenerateToken(&jwt.TokenIn{UserId: admin.Id, Permission: consts.AdminPermission})
if err != nil {
return nil, ecode.Fail.Sub("生成token失败")
}
out = &model.LoginOut{
Token: token,
}
@ -55,5 +79,12 @@ func (s *sAdmin) Info(ctx context.Context, in *model.AdminInfoIn) (out *model.Ad
if !exist {
return nil, ecode.Params.Sub("该用户不存在")
}
var admin entity.Admins
if err := dao.Admins.Ctx(ctx).WherePri(in.Id).Scan(&admin); err != nil {
return nil, ecode.Fail.Sub("查询管理员失败")
}
out = &model.AdminInfoOut{
Username: admin.Username,
}
return
}

View File

@ -1 +1,102 @@
package user
package internal
import (
"context"
"server/internal/consts"
"server/internal/dao"
"server/internal/model"
"server/internal/model/do"
"server/internal/model/entity"
"server/internal/service"
"server/utility/ecode"
"server/utility/jwt"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/grand"
)
type sUser struct{}
func init() {
service.RegisterUser(New())
}
func New() service.IUser {
return &sUser{}
}
func (s *sUser) Login(ctx context.Context, in *model.UserLoginIn) (out *model.UserLoginOut, err error) {
// 根据 OpenId 查找用户
exist, err := dao.Users.Ctx(ctx).Where(do.Users{WxOpenId: in.OpenId}).Exist()
if err != nil {
return nil, ecode.Fail.Sub("查找用户失败")
}
var userId int64
if !exist {
// 用户不存在,创建新用户
user := &entity.Users{
WxOpenId: in.OpenId,
Username: grand.Digits(10),
FirstVisitAt: gtime.Now(),
}
result, err := dao.Users.Ctx(ctx).Insert(user)
if err != nil {
return nil, ecode.Fail.Sub("创建用户失败")
}
userId, err = result.LastInsertId()
if err != nil {
return nil, ecode.Fail.Sub("获取用户ID失败")
}
} else {
// 用户存在,更新最后登录时间
var user entity.Users
if err := dao.Users.Ctx(ctx).Where(do.Users{WxOpenId: in.OpenId}).Scan(&user); err != nil {
return nil, ecode.Fail.Sub("查找用户失败")
}
userId = user.Id
if _, err := dao.Users.Ctx(ctx).Where(do.Users{Id: userId}).Update(do.Users{LastLoginAt: gtime.Now()}); err != nil {
return nil, ecode.Fail.Sub("更新登录时间失败")
}
}
// 生成 token
token, err := jwt.GenerateToken(&jwt.TokenIn{
UserId: userId,
Permission: consts.UserPermission,
})
if err != nil {
return nil, ecode.Fail.Sub("生成token失败")
}
out = &model.UserLoginOut{
Token: token,
}
return
}
func (s *sUser) Info(ctx context.Context, in *model.UserInfoIn) (out *model.UserInfoOut, err error) {
exist, err := dao.Users.Ctx(ctx).Where(do.Users{WxOpenId: in.OpenId, Id: in.Id}).OmitEmptyWhere().Exist()
if err != nil {
return nil, ecode.Fail.Sub("查找用户失败")
}
if !exist {
return nil, ecode.Params.Sub("用户不存在")
}
var user entity.Users
if err := dao.Users.Ctx(ctx).Where(do.Users{WxOpenId: in.OpenId, Id: in.Id}).OmitEmptyWhere().Scan(&user); err != nil {
return nil, ecode.Fail.Sub("查找用户失败")
}
out = &model.UserInfoOut{
Id: user.Id,
}
return
}
func (s *sUser) Update(ctx context.Context, in *model.UserUpdateIn) (out *model.UpdateOut, err error) {
return
}
func (s *sUser) BindPhone(ctx context.Context, in *model.UserBindPhoneIn) (out *model.UpdateOut, err error) {
return
}

View File

@ -27,7 +27,7 @@ import (
// - 若 Token 格式非法或解析失败:终止请求并返回错误。
// - 若 Token 合法:将用户信息写入上下文,继续执行下一个中间件或处理函数。
func Auth(r *ghttp.Request) {
token := r.Header.Get("Authorization")
token := r.GetHeader("Authorization")
ctx := r.GetCtx()
if token == "" {
glog.Infof(ctx, "未登录用户访问: %s %s", r.URL.Path, r.Method)

View File

@ -1,28 +1,27 @@
package middleware
import (
"github.com/gogf/gf/v2/net/ghttp"
"server/utility/ecode"
"server/utility/myCasbin"
)
// Casbin 是用于访问权限控制的中间件。
//
//import (
// "github.com/gogf/gf/v2/net/ghttp"
// "server/utility/ecode"
// "server/utility/myCasbin"
//)
// 该中间件基于 Casbin 权限控制框架,校验当前用户是否有权访问指定的 URL 和请求方法。
// 用户权限从请求上下文中的 "permission" 字段获取,该字段通常由前置中间件(如 Auth注入。
//
//// Casbin 是用于访问权限控制的中间件。
////
//// 该中间件基于 Casbin 权限控制框架,校验当前用户是否有权访问指定的 URL 和请求方法
//// 用户权限从请求上下文中的 "permission" 字段获取,该字段通常由前置中间件(如 Auth注入。
////
//// 参数:
////
//// r *ghttp.Request - 当前的 HTTP 请求对象,由框架自动传入。
////
//// 行为:
//// - 如果权限验证未通过终止请求返回权限不足的错误ecode.Denied)。
//// - 如果权限验证通过:继续执行后续中间件或处理逻辑。
//func Casbin(r *ghttp.Request) {
// permission := r.GetCtxVar("permission").String()
// if !myCasbin.GetMyCasbin().HasPermission(permission, r.URL.Path, r.Method) {
// Exit(r, ecode.Denied)
// }
// r.Middleware.Next()
//}
// 参数:
//
// r *ghttp.Request - 当前的 HTTP 请求对象,由框架自动传入
//
// 行为:
// - 如果权限验证未通过终止请求返回权限不足的错误ecode.Denied
// - 如果权限验证通过:继续执行后续中间件或处理逻辑。
func Casbin(r *ghttp.Request) {
permission := r.GetCtxVar("permission").String()
if !myCasbin.GetMyCasbin().HasPermission(permission, r.URL.Path, r.Method) {
Exit(r, ecode.Denied)
}
r.Middleware.Next()
}

View File

@ -10,6 +10,6 @@ type (
Id int
}
AdminInfoOut struct {
Token string
Username string
}
)

View File

@ -3,3 +3,14 @@ package model
type LoginOut struct {
Token string
}
type UpdateOut struct {
Success bool
}
type DeleteOut struct {
Success bool
}
type CreateOut struct {
Id int64
}

View File

@ -18,10 +18,7 @@ type Admins struct {
RealName interface{} // 真实姓名
Phone interface{} // 手机号
Email interface{} // 邮箱
Role interface{} // 角色1=超级管理员2=运营管理员3=客服管理员4=财务管理员
Status interface{} // 状态1=正常2=禁用
LastLoginAt *gtime.Time // 最后登录时间
LastLoginIp interface{} // 最后登录IP
CreatedAt *gtime.Time // 创建时间
UpdatedAt *gtime.Time // 更新时间
DeletedAt *gtime.Time // 软删除时间戳

View File

@ -0,0 +1,23 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// RewardTypes is the golang structure of table reward_types for DAO operations like Where/Data.
type RewardTypes struct {
g.Meta `orm:"table:reward_types, do:true"`
Id interface{} // 类型ID
Name interface{} // 类型名称
Code interface{} // 类型编码
Description interface{} // 类型描述
Status interface{} // 状态1=启用2=禁用
CreatedAt *gtime.Time // 创建时间
UpdatedAt *gtime.Time // 更新时间
DeletedAt *gtime.Time // 软删除时间
}

View File

@ -11,15 +11,15 @@ import (
// StoreRewards is the golang structure of table store_rewards for DAO operations like Where/Data.
type StoreRewards struct {
g.Meta `orm:"table:store_rewards, do:true"`
Id interface{} // 门店奖励ID
StoreId interface{} // 所属门店ID
RewardType interface{} // 奖励类型1=积分2=优惠券3=商品4=抽奖券
RewardName interface{} // 奖励名称
Amount interface{} // 奖励数量
Total interface{} // 该奖励总库存NULL 表示无限)
MerchantId interface{} // 所属商户ID
CreatedAt *gtime.Time // 创建时间
UpdatedAt *gtime.Time // 更新时间
DeletedAt *gtime.Time // 软删除时间戳
g.Meta `orm:"table:store_rewards, do:true"`
Id interface{} // 门店奖励ID
StoreId interface{} // 所属门店ID
RewardTypeId interface{} // 奖励类型ID
RewardName interface{} // 奖励名称
Amount interface{} // 奖励数量
Total interface{} // 该奖励总库存NULL 表示无限)
MerchantId interface{} // 所属商户ID
CreatedAt *gtime.Time // 创建时间
UpdatedAt *gtime.Time // 更新时间
DeletedAt *gtime.Time // 软删除时间戳
}

View File

@ -15,10 +15,8 @@ type UserLoginRecords struct {
Id interface{} // 记录ID
UserId interface{} // 用户ID
StoreId interface{} // 登录门店ID
MerchantId interface{} // 所属商户ID
LoginIp interface{} // 登录IP地址
LoginDevice interface{} // 登录设备信息
LoginPlatform interface{} // 登录平台1=Web2=iOS3=Android4=微信小程序5=支付宝小程序6=其他
LoginPlatform interface{} // 登录平台1=PC
LoginType interface{} // 登录方式1=微信2=手机号3=账号密码4=其他
LoginStatus interface{} // 登录状态1=成功2=失败
FailReason interface{} // 失败原因

View File

@ -10,17 +10,14 @@ import (
// Admins is the golang structure for table admins.
type Admins struct {
Id int64 `json:"id" orm:"id" description:"管理员ID"` // 管理员ID
Username string `json:"username" orm:"username" description:"管理员用户名"` // 管理员用户名
PasswordHash string `json:"passwordHash" orm:"password_hash" description:"密码哈希"` // 密码哈希
RealName string `json:"realName" orm:"real_name" description:"真实姓名"` // 真实姓名
Phone string `json:"phone" orm:"phone" description:"手机号"` // 手机号
Email string `json:"email" orm:"email" description:"邮箱"` // 邮箱
Role int `json:"role" orm:"role" description:"角色1=超级管理员2=运营管理员3=客服管理员4=财务管理员"` // 角色1=超级管理员2=运营管理员3=客服管理员4=财务管理员
Status int `json:"status" orm:"status" description:"状态1=正常2=禁用"` // 状态1=正常2=禁用
LastLoginAt *gtime.Time `json:"lastLoginAt" orm:"last_login_at" description:"最后登录时间"` // 最后登录时间
LastLoginIp string `json:"lastLoginIp" orm:"last_login_ip" description:"最后登录IP"` // 最后登录IP
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间戳"` // 软删除时间戳
Id int64 `json:"id" orm:"id" description:"管理员ID"` // 管理员ID
Username string `json:"username" orm:"username" description:"管理员用户名"` // 管理员用户名
PasswordHash string `json:"passwordHash" orm:"password_hash" description:"密码哈希"` // 密码哈希
RealName string `json:"realName" orm:"real_name" description:"真实姓名"` // 真实姓名
Phone string `json:"phone" orm:"phone" description:"手机号"` // 手机号
Email string `json:"email" orm:"email" description:"邮箱"` // 邮箱
Status int `json:"status" orm:"status" description:"状态1=正常2=禁用"` // 状态1=正常2=禁用
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间戳"` // 软删除时间戳
}

View File

@ -0,0 +1,21 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// RewardTypes is the golang structure for table reward_types.
type RewardTypes struct {
Id int64 `json:"id" orm:"id" description:"类型ID"` // 类型ID
Name string `json:"name" orm:"name" description:"类型名称"` // 类型名称
Code string `json:"code" orm:"code" description:"类型编码"` // 类型编码
Description string `json:"description" orm:"description" description:"类型描述"` // 类型描述
Status int `json:"status" orm:"status" description:"状态1=启用2=禁用"` // 状态1=启用2=禁用
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间"` // 软删除时间
}

View File

@ -10,14 +10,14 @@ import (
// StoreRewards is the golang structure for table store_rewards.
type StoreRewards struct {
Id int64 `json:"id" orm:"id" description:"门店奖励ID"` // 门店奖励ID
StoreId int64 `json:"storeId" orm:"store_id" description:"所属门店ID"` // 所属门店ID
RewardType int `json:"rewardType" orm:"reward_type" description:"奖励类型1=积分2=优惠券3=商品4=抽奖券"` // 奖励类型1=积分2=优惠券3=商品4=抽奖券
RewardName string `json:"rewardName" orm:"reward_name" description:"奖励名称"` // 奖励名称
Amount int `json:"amount" orm:"amount" description:"奖励数量"` // 奖励数量
Total int `json:"total" orm:"total" description:"该奖励总库存NULL 表示无限)"` // 该奖励总库存NULL 表示无限)
MerchantId int64 `json:"merchantId" orm:"merchant_id" description:"所属商户ID"` // 所属商户ID
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间戳"` // 软删除时间戳
Id int64 `json:"id" orm:"id" description:"门店奖励ID"` // 门店奖励ID
StoreId int64 `json:"storeId" orm:"store_id" description:"所属门店ID"` // 所属门店ID
RewardTypeId int64 `json:"rewardTypeId" orm:"reward_type_id" description:"奖励类型ID"` // 奖励类型ID
RewardName string `json:"rewardName" orm:"reward_name" description:"奖励名称"` // 奖励名称
Amount int `json:"amount" orm:"amount" description:"奖励数量"` // 奖励数量
Total int `json:"total" orm:"total" description:"该奖励总库存NULL 表示无限)"` // 该奖励总库存NULL 表示无限)
MerchantId int64 `json:"merchantId" orm:"merchant_id" description:"所属商户ID"` // 所属商户ID
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间戳"` // 软删除时间戳
}

View File

@ -10,17 +10,15 @@ import (
// UserLoginRecords is the golang structure for table user_login_records.
type UserLoginRecords struct {
Id int64 `json:"id" orm:"id" description:"记录ID"` // 记录ID
UserId int64 `json:"userId" orm:"user_id" description:"用户ID"` // 用户ID
StoreId int64 `json:"storeId" orm:"store_id" description:"登录门店ID"` // 登录门店ID
MerchantId int64 `json:"merchantId" orm:"merchant_id" description:"所属商户ID"` // 所属商户ID
LoginIp string `json:"loginIp" orm:"login_ip" description:"登录IP地址"` // 登录IP地址
LoginDevice string `json:"loginDevice" orm:"login_device" description:"登录设备信息"` // 登录设备信息
LoginPlatform int `json:"loginPlatform" orm:"login_platform" description:"登录平台1=Web2=iOS3=Android4=微信小程序5=支付宝小程序6=其他"` // 登录平台1=Web2=iOS3=Android4=微信小程序5=支付宝小程序6=其他
LoginType int `json:"loginType" orm:"login_type" description:"登录方式1=微信2=手机号3=账号密码4=其他"` // 登录方式1=微信2=手机号3=账号密码4=其他
LoginStatus int `json:"loginStatus" orm:"login_status" description:"登录状态1=成功2=失败"` // 登录状态1=成功2=失败
FailReason string `json:"failReason" orm:"fail_reason" description:"失败原因"` // 失败原因
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"登录时间"` // 登录时间
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间戳"` // 软删除时间戳
Id int64 `json:"id" orm:"id" description:"记录ID"` // 记录ID
UserId int64 `json:"userId" orm:"user_id" description:"用户ID"` // 用户ID
StoreId int64 `json:"storeId" orm:"store_id" description:"登录门店ID"` // 登录门店ID
LoginIp string `json:"loginIp" orm:"login_ip" description:"登录IP地址"` // 登录IP地址
LoginPlatform int `json:"loginPlatform" orm:"login_platform" description:"登录平台1=PC"` // 登录平台1=PC
LoginType int `json:"loginType" orm:"login_type" description:"登录方式1=微信2=手机号3=账号密码4=其他"` // 登录方式1=微信2=手机号3=账号密码4=其他
LoginStatus int `json:"loginStatus" orm:"login_status" description:"登录状态1=成功2=失败"` // 登录状态1=成功2=失败
FailReason string `json:"failReason" orm:"fail_reason" description:"失败原因"` // 失败原因
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"登录时间"` // 登录时间
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间"` // 软删除时间
}

29
internal/model/user.go Normal file
View File

@ -0,0 +1,29 @@
package model
import "time"
type LoginCache struct {
Token string `json:"token"`
Status int `json:"status" dc:"0-准备扫码1-已扫码"`
CreatedAt time.Time `json:"created_at"`
}
type UserLoginIn struct {
OpenId string
}
type UserLoginOut struct {
Token string
}
type UserInfoIn struct {
Id int
OpenId string
}
type UserInfoOut struct {
Id int64
}
type UserUpdateIn struct {
}
type UserBindPhoneIn struct {
}

View File

@ -1,7 +1,8 @@
package packed
import (
//_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
//_ "github.com/gogf/gf/contrib/nosql/redis/v2"
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
_ "server/utility/myCasbin"
_ "server/utility/wechat"
)

View File

@ -5,4 +5,31 @@
package service
type ()
import (
"context"
"server/internal/model"
)
type (
IUser interface {
Login(ctx context.Context, in *model.UserLoginIn) (out *model.UserLoginOut, err error)
Info(ctx context.Context, in *model.UserInfoIn) (out *model.UserInfoOut, err error)
Update(ctx context.Context, in *model.UserUpdateIn) (out *model.UpdateOut, err error)
BindPhone(ctx context.Context, in *model.UserBindPhoneIn) (out *model.UpdateOut, err error)
}
)
var (
localUser IUser
)
func User() IUser {
if localUser == nil {
panic("implement not found for interface IUser, forgot register?")
}
return localUser
}
func RegisterUser(i IUser) {
localUser = i
}