This commit is contained in:
2025-05-09 17:54:28 +08:00
parent 8003431fdb
commit 00c2f07769
110 changed files with 11003 additions and 576 deletions

View File

@@ -7,19 +7,21 @@ import (
type Config struct {
rest.RestConf
DataSource string
CacheRedis cache.CacheConf
JwtAuth JwtAuth // JWT 鉴权相关配置
VerifyCode VerifyCode
Encrypt Encrypt
Alipay AlipayConfig
Wxpay WxpayConfig
Applepay ApplepayConfig
Ali AliConfig
WestConfig WestConfig
YushanConfig YushanConfig
TianjuConfig TianjuConfig
SystemConfig SystemConfig
DataSource string
CacheRedis cache.CacheConf
JwtAuth JwtAuth // JWT 鉴权相关配置
VerifyCode VerifyCode
Encrypt Encrypt
Alipay AlipayConfig
Wxpay WxpayConfig
Applepay ApplepayConfig
Ali AliConfig
WestConfig WestConfig
YushanConfig YushanConfig
TianjuConfig TianjuConfig
SystemConfig SystemConfig
AdminConfig AdminConfig
AdminPromotion AdminPromotion
}
// JwtAuth 用于 JWT 鉴权配置
@@ -88,3 +90,13 @@ type TianjuConfig struct {
type SystemConfig struct {
ThreeVerify bool
}
type AdminConfig struct {
AccessSecret string
AccessExpire int64
RefreshAfter int64
}
type AdminPromotion struct {
URLDomain string
}

View File

@@ -0,0 +1,29 @@
package admin_auth
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_auth"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func AdminLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminLoginReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_auth.NewAdminLoginLogic(r.Context(), svcCtx)
resp, err := l.AdminLogin(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_menu
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_menu"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func CreateMenuHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CreateMenuReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_menu.NewCreateMenuLogic(r.Context(), svcCtx)
resp, err := l.CreateMenu(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_menu
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_menu"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func DeleteMenuHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DeleteMenuReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_menu.NewDeleteMenuLogic(r.Context(), svcCtx)
resp, err := l.DeleteMenu(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_menu
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_menu"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func GetMenuAllHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetMenuAllReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_menu.NewGetMenuAllLogic(r.Context(), svcCtx)
resp, err := l.GetMenuAll(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_menu
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_menu"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func GetMenuDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetMenuDetailReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_menu.NewGetMenuDetailLogic(r.Context(), svcCtx)
resp, err := l.GetMenuDetail(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_menu
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_menu"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func GetMenuListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetMenuListReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_menu.NewGetMenuListLogic(r.Context(), svcCtx)
resp, err := l.GetMenuList(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_menu
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_menu"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func UpdateMenuHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdateMenuReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_menu.NewUpdateMenuLogic(r.Context(), svcCtx)
resp, err := l.UpdateMenu(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_promotion
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_promotion"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func CreatePromotionLinkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CreatePromotionLinkReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_promotion.NewCreatePromotionLinkLogic(r.Context(), svcCtx)
resp, err := l.CreatePromotionLink(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,30 @@
package admin_promotion
import (
"net/http"
"tyc-server/app/main/api/internal/logic/admin_promotion"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func DeletePromotionLinkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DeletePromotionLinkReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_promotion.NewDeletePromotionLinkLogic(r.Context(), svcCtx)
err := l.DeletePromotionLink(&req)
result.HttpResult(r, w, nil, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_promotion
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_promotion"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func GetPromotionLinkDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetPromotionLinkDetailReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_promotion.NewGetPromotionLinkDetailLogic(r.Context(), svcCtx)
resp, err := l.GetPromotionLinkDetail(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_promotion
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_promotion"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func GetPromotionLinkListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetPromotionLinkListReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_promotion.NewGetPromotionLinkListLogic(r.Context(), svcCtx)
resp, err := l.GetPromotionLinkList(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_promotion
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_promotion"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func GetPromotionStatsHistoryHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetPromotionStatsHistoryReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_promotion.NewGetPromotionStatsHistoryLogic(r.Context(), svcCtx)
resp, err := l.GetPromotionStatsHistory(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_promotion
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_promotion"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func GetPromotionStatsTotalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetPromotionStatsTotalReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_promotion.NewGetPromotionStatsTotalLogic(r.Context(), svcCtx)
resp, err := l.GetPromotionStatsTotal(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_promotion
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_promotion"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func RecordLinkClickHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.RecordLinkClickReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_promotion.NewRecordLinkClickLogic(r.Context(), svcCtx)
resp, err := l.RecordLinkClick(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,30 @@
package admin_promotion
import (
"net/http"
"tyc-server/app/main/api/internal/logic/admin_promotion"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func UpdatePromotionLinkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdatePromotionLinkReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_promotion.NewUpdatePromotionLinkLogic(r.Context(), svcCtx)
err := l.UpdatePromotionLink(&req)
result.HttpResult(r, w, nil, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_role
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_role"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func CreateRoleHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CreateRoleReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_role.NewCreateRoleLogic(r.Context(), svcCtx)
resp, err := l.CreateRole(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_role
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_role"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func DeleteRoleHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DeleteRoleReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_role.NewDeleteRoleLogic(r.Context(), svcCtx)
resp, err := l.DeleteRole(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_role
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_role"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func GetRoleDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetRoleDetailReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_role.NewGetRoleDetailLogic(r.Context(), svcCtx)
resp, err := l.GetRoleDetail(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_role
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_role"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func GetRoleListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetRoleListReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_role.NewGetRoleListLogic(r.Context(), svcCtx)
resp, err := l.GetRoleList(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_role
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_role"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func UpdateRoleHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdateRoleReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_role.NewUpdateRoleLogic(r.Context(), svcCtx)
resp, err := l.UpdateRole(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_user
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_user"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func AdminCreateUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminCreateUserReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_user.NewAdminCreateUserLogic(r.Context(), svcCtx)
resp, err := l.AdminCreateUser(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_user
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_user"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func AdminDeleteUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminDeleteUserReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_user.NewAdminDeleteUserLogic(r.Context(), svcCtx)
resp, err := l.AdminDeleteUser(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_user
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_user"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func AdminGetUserDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminGetUserDetailReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_user.NewAdminGetUserDetailLogic(r.Context(), svcCtx)
resp, err := l.AdminGetUserDetail(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,30 @@
package admin_user
import (
"net/http"
"tyc-server/app/main/api/internal/logic/admin_user"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func AdminGetUserListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminGetUserListReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_user.NewAdminGetUserListLogic(r.Context(), svcCtx)
resp, err := l.AdminGetUserList(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_user
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_user"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func AdminUpdateUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminUpdateUserReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_user.NewAdminUpdateUserLogic(r.Context(), svcCtx)
resp, err := l.AdminUpdateUser(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_user
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tyc-server/app/main/api/internal/logic/admin_user"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/result"
"tyc-server/pkg/lzkit/validator"
)
func AdminUserInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminUserInfoReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := admin_user.NewAdminUserInfoLogic(r.Context(), svcCtx)
resp, err := l.AdminUserInfo(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -4,6 +4,11 @@ package handler
import (
"net/http"
admin_auth "tyc-server/app/main/api/internal/handler/admin_auth"
admin_menu "tyc-server/app/main/api/internal/handler/admin_menu"
admin_promotion "tyc-server/app/main/api/internal/handler/admin_promotion"
admin_role "tyc-server/app/main/api/internal/handler/admin_role"
admin_user "tyc-server/app/main/api/internal/handler/admin_user"
auth "tyc-server/app/main/api/internal/handler/auth"
notification "tyc-server/app/main/api/internal/handler/notification"
pay "tyc-server/app/main/api/internal/handler/pay"
@@ -16,6 +21,209 @@ import (
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
// 登录
Method: http.MethodPost,
Path: "/login",
Handler: admin_auth.AdminLoginHandler(serverCtx),
},
},
rest.WithPrefix("/api/v1/admin/auth"),
)
server.AddRoutes(
[]rest.Route{
{
// 获取所有菜单(树形结构)
Method: http.MethodGet,
Path: "/all",
Handler: admin_menu.GetMenuAllHandler(serverCtx),
},
{
// 创建菜单
Method: http.MethodPost,
Path: "/create",
Handler: admin_menu.CreateMenuHandler(serverCtx),
},
{
// 删除菜单
Method: http.MethodDelete,
Path: "/delete/:id",
Handler: admin_menu.DeleteMenuHandler(serverCtx),
},
{
// 获取菜单详情
Method: http.MethodGet,
Path: "/detail/:id",
Handler: admin_menu.GetMenuDetailHandler(serverCtx),
},
{
// 获取菜单列表
Method: http.MethodGet,
Path: "/list",
Handler: admin_menu.GetMenuListHandler(serverCtx),
},
{
// 更新菜单
Method: http.MethodPut,
Path: "/update/:id",
Handler: admin_menu.UpdateMenuHandler(serverCtx),
},
},
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
rest.WithPrefix("/api/v1/admin/menu"),
)
server.AddRoutes(
[]rest.Route{
{
// 创建推广链接
Method: http.MethodPost,
Path: "/create",
Handler: admin_promotion.CreatePromotionLinkHandler(serverCtx),
},
{
// 删除推广链接
Method: http.MethodDelete,
Path: "/delete/:id",
Handler: admin_promotion.DeletePromotionLinkHandler(serverCtx),
},
{
// 获取推广链接详情
Method: http.MethodGet,
Path: "/detail/:id",
Handler: admin_promotion.GetPromotionLinkDetailHandler(serverCtx),
},
{
// 获取推广链接列表
Method: http.MethodGet,
Path: "/list",
Handler: admin_promotion.GetPromotionLinkListHandler(serverCtx),
},
{
// 更新推广链接
Method: http.MethodPut,
Path: "/update/:id",
Handler: admin_promotion.UpdatePromotionLinkHandler(serverCtx),
},
},
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
rest.WithPrefix("/api/v1/admin/promotion/link"),
)
server.AddRoutes(
[]rest.Route{
{
// 记录链接点击
Method: http.MethodGet,
Path: "/record/:path",
Handler: admin_promotion.RecordLinkClickHandler(serverCtx),
},
},
rest.WithPrefix("/api/v1/admin/promotion/link"),
)
server.AddRoutes(
[]rest.Route{
{
// 获取推广历史记录
Method: http.MethodGet,
Path: "/history",
Handler: admin_promotion.GetPromotionStatsHistoryHandler(serverCtx),
},
{
// 获取推广总统计
Method: http.MethodGet,
Path: "/total",
Handler: admin_promotion.GetPromotionStatsTotalHandler(serverCtx),
},
},
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
rest.WithPrefix("/api/v1/admin/promotion/stats"),
)
server.AddRoutes(
[]rest.Route{
{
// 创建角色
Method: http.MethodPost,
Path: "/create",
Handler: admin_role.CreateRoleHandler(serverCtx),
},
{
// 删除角色
Method: http.MethodDelete,
Path: "/delete/:id",
Handler: admin_role.DeleteRoleHandler(serverCtx),
},
{
// 获取角色详情
Method: http.MethodGet,
Path: "/detail/:id",
Handler: admin_role.GetRoleDetailHandler(serverCtx),
},
{
// 获取角色列表
Method: http.MethodGet,
Path: "/list",
Handler: admin_role.GetRoleListHandler(serverCtx),
},
{
// 更新角色
Method: http.MethodPut,
Path: "/update/:id",
Handler: admin_role.UpdateRoleHandler(serverCtx),
},
},
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
rest.WithPrefix("/api/v1/admin/role"),
)
server.AddRoutes(
[]rest.Route{
{
// 创建用户
Method: http.MethodPost,
Path: "/create",
Handler: admin_user.AdminCreateUserHandler(serverCtx),
},
{
// 删除用户
Method: http.MethodDelete,
Path: "/delete/:id",
Handler: admin_user.AdminDeleteUserHandler(serverCtx),
},
{
// 获取用户详情
Method: http.MethodGet,
Path: "/detail/:id",
Handler: admin_user.AdminGetUserDetailHandler(serverCtx),
},
{
// 用户信息
Method: http.MethodGet,
Path: "/info",
Handler: admin_user.AdminUserInfoHandler(serverCtx),
},
{
// 获取用户列表
Method: http.MethodGet,
Path: "/list",
Handler: admin_user.AdminGetUserListHandler(serverCtx),
},
{
// 更新用户
Method: http.MethodPut,
Path: "/update/:id",
Handler: admin_user.AdminUpdateUserHandler(serverCtx),
},
},
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
rest.WithPrefix("/api/v1/admin/user"),
)
server.AddRoutes(
[]rest.Route{
{
@@ -62,21 +270,18 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SourceInterceptor},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/pay/iap_callback",
Handler: pay.IapCallbackHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/pay/payment",
Handler: pay.PaymentHandler(serverCtx),
},
}...,
),
[]rest.Route{
{
Method: http.MethodPost,
Path: "/pay/iap_callback",
Handler: pay.IapCallbackHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/pay/payment",
Handler: pay.PaymentHandler(serverCtx),
},
},
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
rest.WithPrefix("/api/v1"),
)
@@ -99,16 +304,13 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SourceInterceptor},
[]rest.Route{
{
Method: http.MethodGet,
Path: "/render_list/:module",
Handler: product.GetProductRenderListHandler(serverCtx),
},
}...,
),
[]rest.Route{
{
Method: http.MethodGet,
Path: "/render_list/:module",
Handler: product.GetProductRenderListHandler(serverCtx),
},
},
rest.WithPrefix("/api/v1/product"),
)

View File

@@ -0,0 +1,85 @@
package admin_auth
import (
"context"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
jwtx "tyc-server/common/jwt"
"tyc-server/common/xerr"
"tyc-server/pkg/lzkit/crypto"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminLoginLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminLoginLogic {
return &AdminLoginLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminLoginLogic) AdminLogin(req *types.AdminLoginReq) (resp *types.AdminLoginResp, err error) {
// 1. 验证验证码
if !req.Captcha {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码错误"), "用户登录, 验证码错误, 验证码: %v", req.Captcha)
}
// 2. 验证用户名和密码
user, err := l.svcCtx.AdminUserModel.FindOneByUsername(l.ctx, req.Username)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrMsg("用户名或密码错误"), "用户登录, 用户名或密码错误, 用户名: %s", req.Username)
}
// 3. 验证密码
if !crypto.PasswordVerify(req.Password, user.Password) {
return nil, errors.Wrapf(xerr.NewErrMsg("用户名或密码错误"), "用户登录, 用户名或密码错误, 用户名: %s", req.Username)
}
// 4. 获取权限
adminUserRoleBuilder := l.svcCtx.AdminUserRoleModel.SelectBuilder().Where(squirrel.Eq{"user_id": user.Id})
permissions, err := l.svcCtx.AdminUserRoleModel.FindAll(l.ctx, adminUserRoleBuilder, "role_id DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrMsg("获取权限失败"), "用户登录, 获取权限失败, 用户名: %s", req.Username)
}
// 获取角色ID数组
roleIds := make([]int64, 0)
for _, permission := range permissions {
roleIds = append(roleIds, permission.RoleId)
}
// 获取角色名称
roles := make([]string, 0)
for _, roleId := range roleIds {
role, err := l.svcCtx.AdminRoleModel.FindOne(l.ctx, roleId)
if err != nil {
continue
}
roles = append(roles, role.RoleCode)
}
// 5. 生成token
refreshToken := l.svcCtx.Config.JwtAuth.RefreshAfter
expiresAt := l.svcCtx.Config.JwtAuth.AccessExpire
token, err := jwtx.GenerateJwtToken(user.Id, l.svcCtx.Config.JwtAuth.AccessSecret, expiresAt)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrMsg("生成token失败"), "用户登录, 生成token失败, 用户名: %s", req.Username)
}
return &types.AdminLoginResp{
AccessToken: token,
AccessExpire: expiresAt,
RefreshAfter: refreshToken,
Roles: roles,
}, nil
}

View File

@@ -0,0 +1,97 @@
package admin_menu
import (
"context"
"database/sql"
"encoding/json"
"time"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type CreateMenuLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCreateMenuLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateMenuLogic {
return &CreateMenuLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateMenuLogic) CreateMenu(req *types.CreateMenuReq) (resp *types.CreateMenuResp, err error) {
// 1. 参数验证
if req.Name == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("菜单名称不能为空"), "菜单名称不能为空")
}
if req.Type == "menu" && req.Component == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("组件路径不能为空"), "组件路径不能为空")
}
// 2. 检查名称和路径是否重复
exists, err := l.svcCtx.AdminMenuModel.FindOneByNamePath(l.ctx, req.Name, req.Path)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询菜单失败, err: %v", err)
}
if exists != nil {
return nil, errors.Wrapf(xerr.NewErrMsg("菜单名称或路径已存在"), "菜单名称或路径已存在")
}
// 3. 检查父菜单是否存在(如果不是根菜单)
if req.Pid > 0 {
parentMenu, err := l.svcCtx.AdminMenuModel.FindOne(l.ctx, req.Pid)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询父菜单失败, id: %d, err: %v", req.Pid, err)
}
if parentMenu == nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "父菜单不存在, id: %d", req.Pid)
}
}
// 4. 将类型标签转换为值
typeValue, err := l.svcCtx.DictService.GetDictValue(l.ctx, "admin_menu_type", req.Type)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "菜单类型无效: %v", err)
}
// 5. 创建菜单记录
menu := &model.AdminMenu{
Pid: req.Pid,
Name: req.Name,
Path: req.Path,
Component: req.Component,
Redirect: sql.NullString{String: req.Redirect, Valid: req.Redirect != ""},
Status: req.Status,
Type: typeValue,
Sort: req.Sort,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
// 将Meta转换为JSON字符串
metaJson, err := json.Marshal(req.Meta)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "Meta数据格式错误: %v", err)
}
menu.Meta = string(metaJson)
// 6. 保存到数据库
_, err = l.svcCtx.AdminMenuModel.Insert(l.ctx, nil, menu)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建菜单失败, err: %v", err)
}
return &types.CreateMenuResp{
Id: menu.Id,
}, nil
}

View File

@@ -0,0 +1,30 @@
package admin_menu
import (
"context"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type DeleteMenuLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDeleteMenuLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteMenuLogic {
return &DeleteMenuLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteMenuLogic) DeleteMenu(req *types.DeleteMenuReq) (resp *types.DeleteMenuResp, err error) {
// todo: add your logic here and delete this line
return
}

View File

@@ -0,0 +1,250 @@
package admin_menu
import (
"context"
"sort"
"strconv"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/ctxdata"
"tyc-server/common/xerr"
"github.com/Masterminds/squirrel"
"github.com/bytedance/sonic"
"github.com/pkg/errors"
"github.com/samber/lo"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mr"
)
type GetMenuAllLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetMenuAllLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMenuAllLogic {
return &GetMenuAllLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetMenuAllLogic) GetMenuAll(req *types.GetMenuAllReq) (resp *[]types.GetMenuAllResp, err error) {
userId, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败, %+v", err)
}
// 使用MapReduceVoid并发获取用户角色
var roleIds []int64
var permissions []*struct {
RoleId int64
}
type UserRoleResult struct {
RoleId int64
}
err = mr.MapReduceVoid(
func(source chan<- interface{}) {
adminUserRoleBuilder := l.svcCtx.AdminUserRoleModel.SelectBuilder().Where(squirrel.Eq{"user_id": userId})
source <- adminUserRoleBuilder
},
func(item interface{}, writer mr.Writer[*UserRoleResult], cancel func(error)) {
builder := item.(squirrel.SelectBuilder)
result, err := l.svcCtx.AdminUserRoleModel.FindAll(l.ctx, builder, "role_id DESC")
if err != nil {
cancel(errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户角色信息失败, %+v", err))
return
}
for _, r := range result {
writer.Write(&UserRoleResult{RoleId: r.RoleId})
}
},
func(pipe <-chan *UserRoleResult, cancel func(error)) {
for item := range pipe {
permissions = append(permissions, &struct{ RoleId int64 }{RoleId: item.RoleId})
}
},
)
if err != nil {
return nil, err
}
for _, permission := range permissions {
roleIds = append(roleIds, permission.RoleId)
}
// 使用MapReduceVoid并发获取角色菜单
var menuIds []int64
var roleMenus []*struct {
MenuId int64
}
type RoleMenuResult struct {
MenuId int64
}
err = mr.MapReduceVoid(
func(source chan<- interface{}) {
getRoleMenuBuilder := l.svcCtx.AdminRoleMenuModel.SelectBuilder().Where(squirrel.Eq{"role_id": roleIds})
source <- getRoleMenuBuilder
},
func(item interface{}, writer mr.Writer[*RoleMenuResult], cancel func(error)) {
builder := item.(squirrel.SelectBuilder)
result, err := l.svcCtx.AdminRoleMenuModel.FindAll(l.ctx, builder, "id DESC")
if err != nil {
cancel(errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取角色菜单信息失败, %+v", err))
return
}
for _, r := range result {
writer.Write(&RoleMenuResult{MenuId: r.MenuId})
}
},
func(pipe <-chan *RoleMenuResult, cancel func(error)) {
for item := range pipe {
roleMenus = append(roleMenus, &struct{ MenuId int64 }{MenuId: item.MenuId})
}
},
)
if err != nil {
return nil, err
}
for _, roleMenu := range roleMenus {
menuIds = append(menuIds, roleMenu.MenuId)
}
// 使用MapReduceVoid并发获取菜单
type AdminMenuStruct struct {
Id int64
Pid int64
Name string
Path string
Component string
Redirect struct {
String string
Valid bool
}
Meta string
Sort int64
Type int64
Status int64
}
var menus []*AdminMenuStruct
err = mr.MapReduceVoid(
func(source chan<- interface{}) {
adminMenuBuilder := l.svcCtx.AdminMenuModel.SelectBuilder().Where(squirrel.Eq{"id": menuIds})
source <- adminMenuBuilder
},
func(item interface{}, writer mr.Writer[*AdminMenuStruct], cancel func(error)) {
builder := item.(squirrel.SelectBuilder)
result, err := l.svcCtx.AdminMenuModel.FindAll(l.ctx, builder, "sort ASC")
if err != nil {
cancel(errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取菜单信息失败, %+v", err))
return
}
for _, r := range result {
menu := &AdminMenuStruct{
Id: r.Id,
Pid: r.Pid,
Name: r.Name,
Path: r.Path,
Component: r.Component,
Redirect: r.Redirect,
Meta: r.Meta,
Sort: r.Sort,
Type: r.Type,
Status: r.Status,
}
writer.Write(menu)
}
},
func(pipe <-chan *AdminMenuStruct, cancel func(error)) {
for item := range pipe {
menus = append(menus, item)
}
},
)
if err != nil {
return nil, err
}
// 转换为types.Menu结构并存储到映射表
menuMap := make(map[string]types.GetMenuAllResp)
for _, menu := range menus {
// 只处理状态正常的菜单
if menu.Status != 1 {
continue
}
meta := make(map[string]interface{})
err = sonic.Unmarshal([]byte(menu.Meta), &meta)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解析菜单Meta信息失败, %+v", err)
}
redirect := func() string {
if menu.Redirect.Valid {
return menu.Redirect.String
}
return ""
}()
menuId := strconv.FormatInt(menu.Id, 10)
menuMap[menuId] = types.GetMenuAllResp{
Name: menu.Name,
Path: menu.Path,
Redirect: redirect,
Component: menu.Component,
Sort: menu.Sort,
Meta: meta,
Children: make([]types.GetMenuAllResp, 0),
}
}
// 按ParentId将菜单分组
menuGroups := lo.GroupBy(menus, func(item *AdminMenuStruct) int64 {
return item.Pid
})
// 递归构建菜单树
var buildMenuTree func(parentId int64) []types.GetMenuAllResp
buildMenuTree = func(parentId int64) []types.GetMenuAllResp {
children := make([]types.GetMenuAllResp, 0)
childMenus, ok := menuGroups[parentId]
if !ok {
return children
}
// 按Sort排序
sort.Slice(childMenus, func(i, j int) bool {
return childMenus[i].Sort < childMenus[j].Sort
})
for _, childMenu := range childMenus {
menuId := strconv.FormatInt(childMenu.Id, 10)
if menu, exists := menuMap[menuId]; exists && childMenu.Status == 1 {
// 递归构建子菜单
menu.Children = buildMenuTree(childMenu.Id)
children = append(children, menu)
}
}
return children
}
// 从根菜单开始构建ParentId为0的是根菜单
menuTree := buildMenuTree(0)
return &menuTree, nil
}

View File

@@ -0,0 +1,30 @@
package admin_menu
import (
"context"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetMenuDetailLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetMenuDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMenuDetailLogic {
return &GetMenuDetailLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetMenuDetailLogic) GetMenuDetail(req *types.GetMenuDetailReq) (resp *types.GetMenuDetailResp, err error) {
// todo: add your logic here and delete this line
return
}

View File

@@ -0,0 +1,109 @@
package admin_menu
import (
"context"
"encoding/json"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type GetMenuListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetMenuListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMenuListLogic {
return &GetMenuListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetMenuListLogic) GetMenuList(req *types.GetMenuListReq) ([]types.MenuListItem, error) {
// 构建查询条件
builder := l.svcCtx.AdminMenuModel.SelectBuilder()
// 添加筛选条件
if len(req.Name) > 0 {
builder = builder.Where("name LIKE ?", "%"+req.Name+"%")
}
if len(req.Path) > 0 {
builder = builder.Where("path LIKE ?", "%"+req.Path+"%")
}
if req.Status != -1 {
builder = builder.Where("status = ?", req.Status)
}
if req.Type != "" {
builder = builder.Where("type = ?", req.Type)
}
// 排序但不分页,获取所有符合条件的菜单
builder = builder.OrderBy("sort ASC")
// 获取所有菜单
menus, err := l.svcCtx.AdminMenuModel.FindAll(l.ctx, builder, "id ASC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询菜单失败, err: %v", err)
}
// 将菜单按ID存入map
menuMap := make(map[int64]types.MenuListItem)
for _, menu := range menus {
var meta map[string]interface{}
err := json.Unmarshal([]byte(menu.Meta), &meta)
if err != nil {
logx.Errorf("解析Meta字段失败: %v", err)
meta = make(map[string]interface{})
}
menuType, err := l.svcCtx.DictService.GetDictLabel(l.ctx, "admin_menu_type", menu.Type)
if err != nil {
logx.Errorf("获取菜单类型失败: %v", err)
menuType = ""
}
item := types.MenuListItem{
Id: menu.Id,
Pid: menu.Pid,
Name: menu.Name,
Path: menu.Path,
Component: menu.Component,
Redirect: menu.Redirect.String,
Meta: meta,
Status: menu.Status,
Type: menuType,
Sort: menu.Sort,
CreateTime: menu.CreateTime.Format("2006-01-02 15:04:05"),
Children: make([]types.MenuListItem, 0),
}
menuMap[menu.Id] = item
}
// 构建父子关系
for _, menu := range menus {
if menu.Pid > 0 {
// 找到父菜单
if parent, exists := menuMap[menu.Pid]; exists {
// 添加当前菜单到父菜单的子菜单列表
children := append(parent.Children, menuMap[menu.Id])
parent.Children = children
menuMap[menu.Pid] = parent
}
}
}
// 提取顶级菜单ParentId为0到响应列表
result := make([]types.MenuListItem, 0)
for _, menu := range menus {
if menu.Pid == 0 {
result = append(result, menuMap[menu.Id])
}
}
return result, nil
}

View File

@@ -0,0 +1,96 @@
package admin_menu
import (
"context"
"database/sql"
"encoding/json"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type UpdateMenuLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUpdateMenuLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateMenuLogic {
return &UpdateMenuLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateMenuLogic) UpdateMenu(req *types.UpdateMenuReq) (resp *types.UpdateMenuResp, err error) {
// 1. 检查菜单是否存在
menu, err := l.svcCtx.AdminMenuModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询菜单失败, id: %d, err: %v", req.Id, err)
}
if menu == nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "菜单不存在, id: %d", req.Id)
}
// 2. 将类型标签转换为值
typeValue, err := l.svcCtx.DictService.GetDictValue(l.ctx, "admin_menu_type", req.Type)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "菜单类型无效: %v", err)
}
// 3. 检查父菜单是否存在(如果不是根菜单)
if req.Pid > 0 {
parentMenu, err := l.svcCtx.AdminMenuModel.FindOne(l.ctx, req.Pid)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询父菜单失败, id: %d, err: %v", req.Pid, err)
}
if parentMenu == nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "父菜单不存在, id: %d", req.Pid)
}
}
// 4. 检查名称和路径是否重复
if req.Name != menu.Name || req.Path != menu.Path {
exists, err := l.svcCtx.AdminMenuModel.FindOneByNamePath(l.ctx, req.Name, req.Path)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询菜单失败, err: %v", err)
}
if exists != nil && exists.Id != req.Id {
return nil, errors.Wrapf(xerr.NewErrMsg("菜单名称或路径已存在"), "菜单名称或路径已存在")
}
}
// 5. 更新菜单信息
menu.Pid = req.Pid
menu.Name = req.Name
menu.Path = req.Path
menu.Component = req.Component
menu.Redirect = sql.NullString{String: req.Redirect, Valid: req.Redirect != ""}
menu.Status = req.Status
menu.Type = typeValue
menu.Sort = req.Sort
// 将Meta转换为JSON字符串
metaJson, err := json.Marshal(req.Meta)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "Meta数据格式错误: %v", err)
}
menu.Meta = string(metaJson)
// 6. 保存更新
_, err = l.svcCtx.AdminMenuModel.Update(l.ctx, nil, menu)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新菜单失败, err: %v", err)
}
return &types.UpdateMenuResp{
Success: true,
}, nil
}

View File

@@ -0,0 +1,136 @@
package admin_promotion
import (
"context"
"crypto/rand"
"fmt"
"math/big"
"time"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/ctxdata"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type CreatePromotionLinkLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCreatePromotionLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreatePromotionLinkLogic {
return &CreatePromotionLinkLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
// 生成6位随机字符串大小写字母和数字
func generateRandomString() (string, error) {
const (
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
length = 6
)
result := make([]byte, length)
for i := 0; i < length; i++ {
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(chars))))
if err != nil {
return "", err
}
result[i] = chars[num.Int64()]
}
return string(result), nil
}
func (l *CreatePromotionLinkLogic) CreatePromotionLink(req *types.CreatePromotionLinkReq) (resp *types.CreatePromotionLinkResp, err error) {
// 获取当前用户ID
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
if getUidErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建推广链接, 获取用户信息失败, %+v", getUidErr)
}
// 生成唯一URL
var url string
maxRetries := 5 // 最大重试次数
for i := 0; i < maxRetries; i++ {
// 生成6位随机字符串
randomStr, err := generateRandomString()
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建推广链接, 生成随机字符串失败, %+v", err)
}
// 检查URL是否已存在
existLink, err := l.svcCtx.AdminPromotionLinkModel.FindOneByUrl(l.ctx, randomStr)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建推广链接, 检查URL是否存在失败, %+v", err)
}
if existLink != nil {
continue // URL已存在继续尝试
}
// URL可用
url = randomStr
break
}
if url == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建推广链接失败, 多次尝试生成唯一URL均失败")
}
url = fmt.Sprintf("%s/%s", l.svcCtx.Config.AdminPromotion.URLDomain, url)
// 创建推广链接
link := &model.AdminPromotionLink{
Name: req.Name,
Url: url,
AdminUserId: adminUserId,
}
var linkId int64
err = l.svcCtx.AdminPromotionLinkModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
result, err := l.svcCtx.AdminPromotionLinkModel.Insert(l.ctx, session, link)
if err != nil {
return fmt.Errorf("创建推广链接失败, %+v", err)
}
linkId, err = result.LastInsertId()
if err != nil {
return fmt.Errorf("获取推广链接ID失败, %+v", err)
}
// 创建总统计记录
totalStats := &model.AdminPromotionLinkStatsTotal{
LinkId: linkId,
}
_, err = l.svcCtx.AdminPromotionLinkStatsTotalModel.Insert(l.ctx, session, totalStats)
if err != nil {
return fmt.Errorf("创建推广链接总统计记录失败, %+v", err)
}
// 创建统计历史记录
historyStats := &model.AdminPromotionLinkStatsHistory{
LinkId: linkId,
StatsDate: time.Now().Truncate(24 * time.Hour),
}
_, err = l.svcCtx.AdminPromotionLinkStatsHistoryModel.Insert(l.ctx, session, historyStats)
if err != nil {
return fmt.Errorf("创建推广链接统计历史记录失败, %+v", err)
}
return nil
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建推广链接失败, %+v", err)
}
return &types.CreatePromotionLinkResp{
Id: linkId,
Url: url,
}, nil
}

View File

@@ -0,0 +1,91 @@
package admin_promotion
import (
"context"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/ctxdata"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type DeletePromotionLinkLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDeletePromotionLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeletePromotionLinkLogic {
return &DeletePromotionLinkLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeletePromotionLinkLogic) DeletePromotionLink(req *types.DeletePromotionLinkReq) error {
// 获取当前用户ID
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
if getUidErr != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "删除推广链接, 获取用户信息失败, %+v", getUidErr)
}
// 获取链接信息
link, err := l.svcCtx.AdminPromotionLinkModel.FindOne(l.ctx, req.Id)
if err != nil {
return errors.Wrapf(err, "删除推广链接, 获取链接信息失败, %+v", err)
}
// 验证用户权限
if link.AdminUserId != adminUserId {
return errors.Wrapf(xerr.NewErrMsg("无权限删除此链接"), "删除推广链接, 无权限删除此链接, %+v", link)
}
// 在事务中执行所有删除操作
err = l.svcCtx.AdminPromotionLinkModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 软删除链接
err = l.svcCtx.AdminPromotionLinkModel.DeleteSoft(l.ctx, session, link)
if err != nil {
return errors.Wrapf(err, "删除推广链接, 软删除链接失败, %+v", err)
}
// 软删除总统计记录
totalStats, err := l.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(l.ctx, link.Id)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return errors.Wrapf(err, "删除推广链接, 获取总统计记录失败, %+v", err)
}
if totalStats != nil {
err = l.svcCtx.AdminPromotionLinkStatsTotalModel.DeleteSoft(l.ctx, session, totalStats)
if err != nil {
return errors.Wrapf(err, "删除推广链接, 软删除总统计记录失败, %+v", err)
}
}
// 软删除历史统计记录
builder := l.svcCtx.AdminPromotionLinkStatsHistoryModel.SelectBuilder()
builder = builder.Where("link_id = ?", link.Id)
historyStats, err := l.svcCtx.AdminPromotionLinkStatsHistoryModel.FindAll(l.ctx, builder, "")
if err != nil {
return errors.Wrapf(err, "删除推广链接, 获取历史统计记录失败, %+v", err)
}
for _, stat := range historyStats {
err = l.svcCtx.AdminPromotionLinkStatsHistoryModel.DeleteSoft(l.ctx, session, stat)
if err != nil {
return errors.Wrapf(err, "删除推广链接, 软删除历史统计记录失败, %+v", err)
}
}
return nil
})
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "删除推广链接失败, %+v", err)
}
return nil
}

View File

@@ -0,0 +1,65 @@
package admin_promotion
import (
"context"
"fmt"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/ctxdata"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type GetPromotionLinkDetailLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetPromotionLinkDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPromotionLinkDetailLogic {
return &GetPromotionLinkDetailLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetPromotionLinkDetailLogic) GetPromotionLinkDetail(req *types.GetPromotionLinkDetailReq) (resp *types.GetPromotionLinkDetailResp, err error) {
// 获取当前用户ID
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
if getUidErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取当前用户ID失败, %+v", getUidErr)
}
// 获取链接信息
link, err := l.svcCtx.AdminPromotionLinkModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(err, "获取链接信息失败, %+v", err)
}
// 验证用户权限
if link.AdminUserId != adminUserId {
return nil, errors.Wrapf(xerr.NewErrMsg("无权限访问此链接"), "获取链接信息失败, 无权限访问此链接, %+v", link)
}
// 获取总统计
totalStats, err := l.svcCtx.AdminPromotionLinkStatsTotalModel.FindOne(l.ctx, link.Id)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(err, "获取总统计失败, %+v", err)
}
return &types.GetPromotionLinkDetailResp{
Name: link.Name,
Url: link.Url,
ClickCount: totalStats.ClickCount,
PayCount: totalStats.PayCount,
PayAmount: fmt.Sprintf("%.2f", totalStats.PayAmount),
CreateTime: link.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: link.UpdateTime.Format("2006-01-02 15:04:05"),
LastClickTime: totalStats.LastClickTime.Time.Format("2006-01-02 15:04:05"),
LastPayTime: totalStats.LastPayTime.Time.Format("2006-01-02 15:04:05"),
}, nil
}

View File

@@ -0,0 +1,104 @@
package admin_promotion
import (
"context"
"fmt"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/ctxdata"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mr"
)
type GetPromotionLinkListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetPromotionLinkListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPromotionLinkListLogic {
return &GetPromotionLinkListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetPromotionLinkListLogic) GetPromotionLinkList(req *types.GetPromotionLinkListReq) (resp *types.GetPromotionLinkListResp, err error) {
// 获取当前用户ID
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
if getUidErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取当前用户ID失败, %+v", getUidErr)
}
// 构建查询条件
builder := l.svcCtx.AdminPromotionLinkModel.SelectBuilder()
builder = builder.Where("admin_user_id = ?", adminUserId)
if req.Name != "" {
builder = builder.Where("name LIKE ?", "%"+req.Name+"%")
}
if req.Url != "" {
builder = builder.Where("url LIKE ?", "%"+req.Url+"%")
}
// 获取列表和总数
links, total, err := l.svcCtx.AdminPromotionLinkModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(err, "获取推广链接列表失败, %+v", err)
}
// 使用MapReduce并发获取统计数据
items := make([]types.PromotionLinkItem, len(links))
err = mr.MapReduceVoid(func(source chan<- interface{}) {
for _, link := range links {
source <- link
}
}, func(item interface{}, writer mr.Writer[types.PromotionLinkItem], cancel func(error)) {
link := item.(*model.AdminPromotionLink)
// 获取总统计
totalStats, err := l.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(l.ctx, link.Id)
if err != nil && !errors.Is(err, model.ErrNotFound) {
cancel(errors.Wrapf(err, "获取总统计失败, linkId: %d, %+v", link.Id, err))
return
}
writer.Write(types.PromotionLinkItem{
Id: link.Id,
Name: link.Name,
Url: link.Url,
ClickCount: totalStats.ClickCount,
PayCount: totalStats.PayCount,
PayAmount: fmt.Sprintf("%.2f", totalStats.PayAmount),
CreateTime: link.CreateTime.Format("2006-01-02 15:04:05"),
LastClickTime: func() string {
if totalStats.LastClickTime.Valid {
return totalStats.LastClickTime.Time.Format("2006-01-02 15:04:05")
}
return ""
}(),
LastPayTime: func() string {
if totalStats.LastPayTime.Valid {
return totalStats.LastPayTime.Time.Format("2006-01-02 15:04:05")
}
return ""
}(),
})
}, func(pipe <-chan types.PromotionLinkItem, cancel func(error)) {
for i := 0; i < len(links); i++ {
item := <-pipe
items[i] = item
}
})
if err != nil {
return nil, errors.Wrapf(err, "获取推广链接统计数据失败, %+v", err)
}
return &types.GetPromotionLinkListResp{
Total: total,
Items: items,
}, nil
}

View File

@@ -0,0 +1,83 @@
package admin_promotion
import (
"context"
"time"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/ctxdata"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type GetPromotionStatsHistoryLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetPromotionStatsHistoryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPromotionStatsHistoryLogic {
return &GetPromotionStatsHistoryLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetPromotionStatsHistoryLogic) GetPromotionStatsHistory(req *types.GetPromotionStatsHistoryReq) (resp []types.PromotionStatsHistoryItem, err error) {
// 获取当前用户ID
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
if getUidErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取当前用户ID失败, %+v", getUidErr)
}
// 构建查询条件
builder := l.svcCtx.AdminPromotionLinkStatsHistoryModel.SelectBuilder()
// 如果有日期范围,添加日期过滤
if req.StartDate != "" && req.EndDate != "" {
startDate, err := time.Parse("2006-01-02", req.StartDate)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "开始日期格式错误")
}
endDate, err := time.Parse("2006-01-02", req.EndDate)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "结束日期格式错误")
}
// 将结束日期设置为当天的最后一刻
endDate = endDate.Add(24*time.Hour - time.Second)
builder = builder.Where("stats_date BETWEEN ? AND ?", startDate, endDate)
}
// 获取历史统计数据
historyStats, err := l.svcCtx.AdminPromotionLinkStatsHistoryModel.FindAll(l.ctx, builder, "stats_date DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取历史统计数据失败")
}
// 转换为响应格式
resp = make([]types.PromotionStatsHistoryItem, 0, len(historyStats))
for _, stat := range historyStats {
// 验证链接是否属于当前用户
link, err := l.svcCtx.AdminPromotionLinkModel.FindOne(l.ctx, stat.LinkId)
if err != nil {
continue // 如果链接不存在,跳过该记录
}
if link.AdminUserId != adminUserId {
continue // 如果链接不属于当前用户,跳过该记录
}
resp = append(resp, types.PromotionStatsHistoryItem{
Id: stat.Id,
LinkId: stat.LinkId,
PayAmount: stat.PayAmount,
ClickCount: stat.ClickCount,
PayCount: stat.PayCount,
StatsDate: stat.StatsDate.Format("2006-01-02"),
})
}
return resp, nil
}

View File

@@ -0,0 +1,165 @@
package admin_promotion
import (
"context"
"time"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/ctxdata"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mr"
)
type GetPromotionStatsTotalLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetPromotionStatsTotalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPromotionStatsTotalLogic {
return &GetPromotionStatsTotalLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetPromotionStatsTotalLogic) GetPromotionStatsTotal(req *types.GetPromotionStatsTotalReq) (resp *types.GetPromotionStatsTotalResp, err error) {
// 获取当前用户ID
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
if getUidErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取当前用户ID失败, %+v", getUidErr)
}
// 获取用户的所有推广链接
linkBuilder := l.svcCtx.AdminPromotionLinkModel.SelectBuilder()
linkBuilder = linkBuilder.Where("admin_user_id = ?", adminUserId)
links, err := l.svcCtx.AdminPromotionLinkModel.FindAll(l.ctx, linkBuilder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取推广链接列表失败, %+v", err)
}
// 如果没有推广链接,返回空统计
if len(links) == 0 {
return &types.GetPromotionStatsTotalResp{}, nil
}
// 构建链接ID列表
linkIds := make([]int64, len(links))
for i, link := range links {
linkIds[i] = link.Id
}
// 获取并计算总统计数据
var totalClickCount, totalPayCount int64
var totalPayAmount float64
err = mr.MapReduceVoid(func(source chan<- interface{}) {
for _, linkId := range linkIds {
source <- linkId
}
}, func(item interface{}, writer mr.Writer[struct {
ClickCount int64
PayCount int64
PayAmount float64
}], cancel func(error)) {
linkId := item.(int64)
stats, err := l.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(l.ctx, linkId)
if err != nil && !errors.Is(err, model.ErrNotFound) {
cancel(errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取总统计数据失败, linkId: %d, %+v", linkId, err))
return
}
if stats != nil {
writer.Write(struct {
ClickCount int64
PayCount int64
PayAmount float64
}{
ClickCount: stats.ClickCount,
PayCount: stats.PayCount,
PayAmount: stats.PayAmount,
})
}
}, func(pipe <-chan struct {
ClickCount int64
PayCount int64
PayAmount float64
}, cancel func(error)) {
for stats := range pipe {
totalClickCount += stats.ClickCount
totalPayCount += stats.PayCount
totalPayAmount += stats.PayAmount
}
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取总统计数据失败, %+v", err)
}
// 获取今日统计数据
today := time.Now().Truncate(24 * time.Hour)
var todayClickCount, todayPayCount int64
var todayPayAmount float64
err = mr.MapReduceVoid(func(source chan<- interface{}) {
for _, linkId := range linkIds {
source <- linkId
}
}, func(item interface{}, writer mr.Writer[struct {
ClickCount int64
PayCount int64
PayAmount float64
}], cancel func(error)) {
linkId := item.(int64)
builder := l.svcCtx.AdminPromotionLinkStatsHistoryModel.SelectBuilder()
builder = builder.Where("link_id = ? AND DATE(stats_date) = DATE(?)", linkId, today)
histories, err := l.svcCtx.AdminPromotionLinkStatsHistoryModel.FindAll(l.ctx, builder, "")
if err != nil {
cancel(errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取今日统计数据失败, linkId: %d, %+v", linkId, err))
return
}
var clickCount, payCount int64
var payAmount float64
for _, history := range histories {
clickCount += history.ClickCount
payCount += history.PayCount
payAmount += history.PayAmount
}
writer.Write(struct {
ClickCount int64
PayCount int64
PayAmount float64
}{
ClickCount: clickCount,
PayCount: payCount,
PayAmount: payAmount,
})
}, func(pipe <-chan struct {
ClickCount int64
PayCount int64
PayAmount float64
}, cancel func(error)) {
for stats := range pipe {
todayClickCount += stats.ClickCount
todayPayCount += stats.PayCount
todayPayAmount += stats.PayAmount
}
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取今日统计数据失败, %+v", err)
}
return &types.GetPromotionStatsTotalResp{
TodayClickCount: int64(todayClickCount),
TodayPayCount: int64(todayPayCount),
TodayPayAmount: todayPayAmount,
TotalClickCount: int64(totalClickCount),
TotalPayCount: int64(totalPayCount),
TotalPayAmount: totalPayAmount,
}, nil
}

View File

@@ -0,0 +1,98 @@
package admin_promotion
import (
"context"
"fmt"
"time"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type RecordLinkClickLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewRecordLinkClickLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RecordLinkClickLogic {
return &RecordLinkClickLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *RecordLinkClickLogic) RecordLinkClick(req *types.RecordLinkClickReq) (resp *types.RecordLinkClickResp, err error) {
// 校验路径格式
if len(req.Path) != 6 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "无效的推广链接路径")
}
// 检查是否只包含大小写字母和数字
for _, char := range req.Path {
if !((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9')) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "无效的推广链接路径")
}
}
url := fmt.Sprintf("%s/%s", l.svcCtx.Config.AdminPromotion.URLDomain, req.Path)
link, err := l.svcCtx.AdminPromotionLinkModel.FindOneByUrl(l.ctx, url)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "无效的推广链接路径")
}
err = l.svcCtx.AdminPromotionLinkModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 更新总统计
totalStats, err := l.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(l.ctx, link.Id)
if err != nil {
return err
}
totalStats.ClickCount = totalStats.ClickCount + 1
err = l.svcCtx.AdminPromotionLinkStatsTotalModel.UpdateWithVersion(l.ctx, session, totalStats)
if err != nil {
return fmt.Errorf("更新总统计失败%+v", err)
}
// 更新历史统计
today := time.Now().Truncate(24 * time.Hour)
builder := l.svcCtx.AdminPromotionLinkStatsHistoryModel.SelectBuilder()
builder = builder.Where("link_id = ? AND DATE(stats_date) = DATE(?)", link.Id, today)
historyStats, err := l.svcCtx.AdminPromotionLinkStatsHistoryModel.FindAll(l.ctx, builder, "")
if err != nil {
return fmt.Errorf("查询今日统计记录失败%+v", err)
}
if len(historyStats) == 0 {
// 创建今天的记录
newStats := &model.AdminPromotionLinkStatsHistory{
LinkId: link.Id,
StatsDate: today,
ClickCount: 1,
}
_, err = l.svcCtx.AdminPromotionLinkStatsHistoryModel.Insert(l.ctx, session, newStats)
if err != nil {
return fmt.Errorf("创建今日统计记录失败%+v", err)
}
} else {
// 更新今日记录
historyStats[0].ClickCount++
err = l.svcCtx.AdminPromotionLinkStatsHistoryModel.UpdateWithVersion(l.ctx, session, historyStats[0])
if err != nil {
return fmt.Errorf("更新历史统计失败%+v", err)
}
}
return nil
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新总统计失败,%+v", err)
}
return &types.RecordLinkClickResp{
Success: true,
}, nil
}

View File

@@ -0,0 +1,57 @@
package admin_promotion
import (
"context"
"time"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/ctxdata"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type UpdatePromotionLinkLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUpdatePromotionLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdatePromotionLinkLogic {
return &UpdatePromotionLinkLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdatePromotionLinkLogic) UpdatePromotionLink(req *types.UpdatePromotionLinkReq) error {
// 获取当前用户ID
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
if getUidErr != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "更新推广链接, 获取用户信息失败, %+v", getUidErr)
}
// 获取链接信息
link, err := l.svcCtx.AdminPromotionLinkModel.FindOne(l.ctx, req.Id)
if err != nil {
return errors.Wrapf(err, "更新推广链接, 获取链接信息失败, %+v", err)
}
// 验证用户权限
if link.AdminUserId != adminUserId {
return errors.Wrapf(xerr.NewErrMsg("无权限修改此链接"), "更新推广链接, 无权限修改此链接, %+v", link)
}
// 更新链接信息
link.Name = *req.Name
link.UpdateTime = time.Now()
_, err = l.svcCtx.AdminPromotionLinkModel.Update(l.ctx, nil, link)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新推广链接, 更新链接信息失败, %+v", err)
}
return nil
}

View File

@@ -0,0 +1,83 @@
package admin_role
import (
"context"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type CreateRoleLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCreateRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateRoleLogic {
return &CreateRoleLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateRoleLogic) CreateRole(req *types.CreateRoleReq) (resp *types.CreateRoleResp, err error) {
// 检查角色编码是否已存在
roleModel, err := l.svcCtx.AdminRoleModel.FindOneByRoleCode(l.ctx, req.RoleCode)
if err != nil && err != model.ErrNotFound {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建角色失败: %v", err)
}
if roleModel != nil && roleModel.RoleName == req.RoleName {
return nil, errors.Wrapf(xerr.NewErrMsg("角色名称已存在"), "创建角色失败, 角色名称已存在: %v", err)
}
// 创建角色
role := &model.AdminRole{
RoleName: req.RoleName,
RoleCode: req.RoleCode,
Description: req.Description,
Status: req.Status,
Sort: req.Sort,
}
var roleId int64
// 使用事务创建角色和关联菜单
err = l.svcCtx.AdminRoleModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 创建角色
result, err := l.svcCtx.AdminRoleModel.Insert(ctx, session, role)
if err != nil {
return errors.New("插入新角色失败")
}
roleId, err = result.LastInsertId()
if err != nil {
return errors.New("获取新角色ID失败")
}
// 创建角色菜单关联
if len(req.MenuIds) > 0 {
for _, menuId := range req.MenuIds {
roleMenu := &model.AdminRoleMenu{
RoleId: roleId,
MenuId: menuId,
}
_, err = l.svcCtx.AdminRoleMenuModel.Insert(ctx, session, roleMenu)
if err != nil {
return errors.New("插入角色菜单关联失败")
}
}
}
return nil
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建角色失败: %v", err)
}
return &types.CreateRoleResp{
Id: roleId,
}, nil
}

View File

@@ -0,0 +1,84 @@
package admin_role
import (
"context"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type DeleteRoleLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDeleteRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteRoleLogic {
return &DeleteRoleLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteRoleLogic) DeleteRole(req *types.DeleteRoleReq) (resp *types.DeleteRoleResp, err error) {
// 检查角色是否存在
_, err = l.svcCtx.AdminRoleModel.FindOne(l.ctx, req.Id)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg("角色不存在"), "删除角色失败, 角色不存在err: %v", err)
}
return nil, err
}
// 使用事务删除角色和关联数据
err = l.svcCtx.AdminRoleModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 删除角色菜单关联
builder := l.svcCtx.AdminRoleMenuModel.SelectBuilder().
Where("role_id = ?", req.Id)
menus, err := l.svcCtx.AdminRoleMenuModel.FindAll(ctx, builder, "id ASC")
if err != nil {
return err
}
for _, menu := range menus {
err = l.svcCtx.AdminRoleMenuModel.Delete(ctx, session, menu.Id)
if err != nil {
return err
}
}
// 删除角色用户关联
builder = l.svcCtx.AdminUserRoleModel.SelectBuilder().
Where("role_id = ?", req.Id)
users, err := l.svcCtx.AdminUserRoleModel.FindAll(ctx, builder, "id ASC")
if err != nil {
return err
}
for _, user := range users {
err = l.svcCtx.AdminUserRoleModel.Delete(ctx, session, user.Id)
if err != nil {
return err
}
}
// 删除角色
err = l.svcCtx.AdminRoleModel.Delete(ctx, session, req.Id)
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "删除角色失败err: %v", err)
}
return &types.DeleteRoleResp{
Success: true,
}, nil
}

View File

@@ -0,0 +1,91 @@
package admin_role
import (
"context"
"sync"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/samber/lo"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mr"
)
type GetRoleDetailLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetRoleDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRoleDetailLogic {
return &GetRoleDetailLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetRoleDetailLogic) GetRoleDetail(req *types.GetRoleDetailReq) (resp *types.GetRoleDetailResp, err error) {
// 使用MapReduceVoid并发获取角色信息和菜单ID
var role *model.AdminRole
var menuIds []int64
var mutex sync.Mutex
var wg sync.WaitGroup
mr.MapReduceVoid(func(source chan<- interface{}) {
source <- 1 // 获取角色信息
source <- 2 // 获取菜单ID
}, func(item interface{}, writer mr.Writer[interface{}], cancel func(error)) {
taskType := item.(int)
wg.Add(1)
defer wg.Done()
if taskType == 1 {
result, err := l.svcCtx.AdminRoleModel.FindOne(l.ctx, req.Id)
if err != nil {
cancel(err)
return
}
mutex.Lock()
role = result
mutex.Unlock()
} else if taskType == 2 {
builder := l.svcCtx.AdminRoleMenuModel.SelectBuilder().
Where("role_id = ?", req.Id)
menus, err := l.svcCtx.AdminRoleMenuModel.FindAll(l.ctx, builder, "id ASC")
if err != nil {
cancel(err)
return
}
mutex.Lock()
menuIds = lo.Map(menus, func(item *model.AdminRoleMenu, _ int) int64 {
return item.MenuId
})
mutex.Unlock()
}
}, func(pipe <-chan interface{}, cancel func(error)) {
// 不需要处理pipe中的数据
})
wg.Wait()
if role == nil {
return nil, errors.Wrapf(xerr.NewErrMsg("角色不存在"), "获取角色详情失败, 角色不存在err: %v", err)
}
return &types.GetRoleDetailResp{
Id: role.Id,
RoleName: role.RoleName,
RoleCode: role.RoleCode,
Description: role.Description,
Status: role.Status,
Sort: role.Sort,
CreateTime: role.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: role.UpdateTime.Format("2006-01-02 15:04:05"),
MenuIds: menuIds,
}, nil
}

View File

@@ -0,0 +1,148 @@
package admin_role
import (
"context"
"sync"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"github.com/samber/lo"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mr"
)
type GetRoleListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetRoleListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRoleListLogic {
return &GetRoleListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetRoleListLogic) GetRoleList(req *types.GetRoleListReq) (resp *types.GetRoleListResp, err error) {
resp = &types.GetRoleListResp{
Items: make([]types.RoleListItem, 0),
Total: 0,
}
// 构建查询条件
builder := l.svcCtx.AdminRoleModel.SelectBuilder()
if len(req.Name) > 0 {
builder = builder.Where("role_name LIKE ?", "%"+req.Name+"%")
}
if len(req.Code) > 0 {
builder = builder.Where("role_code LIKE ?", "%"+req.Code+"%")
}
if req.Status != -1 {
builder = builder.Where("status = ?", req.Status)
}
// 设置分页
offset := (req.Page - 1) * req.PageSize
builder = builder.OrderBy("sort ASC").Limit(uint64(req.PageSize)).Offset(uint64(offset))
// 使用MapReduceVoid并发获取总数和列表数据
var roles []*model.AdminRole
var total int64
var mutex sync.Mutex
var wg sync.WaitGroup
mr.MapReduceVoid(func(source chan<- interface{}) {
source <- 1 // 获取角色列表
source <- 2 // 获取总数
}, func(item interface{}, writer mr.Writer[*model.AdminRole], cancel func(error)) {
taskType := item.(int)
wg.Add(1)
defer wg.Done()
if taskType == 1 {
result, err := l.svcCtx.AdminRoleModel.FindAll(l.ctx, builder, "id DESC")
if err != nil {
cancel(err)
return
}
mutex.Lock()
roles = result
mutex.Unlock()
} else if taskType == 2 {
countBuilder := l.svcCtx.AdminRoleModel.SelectBuilder()
if len(req.Name) > 0 {
countBuilder = countBuilder.Where("role_name LIKE ?", "%"+req.Name+"%")
}
if len(req.Code) > 0 {
countBuilder = countBuilder.Where("role_code LIKE ?", "%"+req.Code+"%")
}
if req.Status != -1 {
countBuilder = countBuilder.Where("status = ?", req.Status)
}
count, err := l.svcCtx.AdminRoleModel.FindCount(l.ctx, countBuilder, "id")
if err != nil {
cancel(err)
return
}
mutex.Lock()
total = count
mutex.Unlock()
}
}, func(pipe <-chan *model.AdminRole, cancel func(error)) {
// 不需要处理pipe中的数据
})
wg.Wait()
// 并发获取每个角色的菜单ID
var roleItems []types.RoleListItem
var roleItemsMutex sync.Mutex
mr.MapReduceVoid(func(source chan<- interface{}) {
for _, role := range roles {
source <- role
}
}, func(item interface{}, writer mr.Writer[[]int64], cancel func(error)) {
role := item.(*model.AdminRole)
// 获取角色关联的菜单ID
builder := l.svcCtx.AdminRoleMenuModel.SelectBuilder().
Where("role_id = ?", role.Id)
menus, err := l.svcCtx.AdminRoleMenuModel.FindAll(l.ctx, builder, "id ASC")
if err != nil {
cancel(err)
return
}
menuIds := lo.Map(menus, func(item *model.AdminRoleMenu, _ int) int64 {
return item.MenuId
})
writer.Write(menuIds)
}, func(pipe <-chan []int64, cancel func(error)) {
for _, role := range roles {
menuIds := <-pipe
item := types.RoleListItem{
Id: role.Id,
RoleName: role.RoleName,
RoleCode: role.RoleCode,
Description: role.Description,
Status: role.Status,
Sort: role.Sort,
CreateTime: role.CreateTime.Format("2006-01-02 15:04:05"),
MenuIds: menuIds,
}
roleItemsMutex.Lock()
roleItems = append(roleItems, item)
roleItemsMutex.Unlock()
}
})
resp.Items = roleItems
resp.Total = total
return resp, nil
}

View File

@@ -0,0 +1,148 @@
package admin_role
import (
"context"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/samber/lo"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type UpdateRoleLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUpdateRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateRoleLogic {
return &UpdateRoleLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateRoleLogic) UpdateRole(req *types.UpdateRoleReq) (resp *types.UpdateRoleResp, err error) {
// 检查角色是否存在
role, err := l.svcCtx.AdminRoleModel.FindOne(l.ctx, req.Id)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg("角色不存在"), "更新角色失败, 角色不存在err: %v", err)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新角色失败err: %v", err)
}
// 检查角色编码是否重复
if req.RoleCode != nil && *req.RoleCode != role.RoleCode {
roleModel, err := l.svcCtx.AdminRoleModel.FindOneByRoleCode(l.ctx, *req.RoleCode)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新角色失败err: %v", err)
}
if roleModel != nil {
return nil, errors.Wrapf(xerr.NewErrMsg("角色编码已存在"), "更新角色失败, 角色编码已存在err: %v", err)
}
}
// 更新角色信息
if req.RoleName != nil {
role.RoleName = *req.RoleName
}
if req.RoleCode != nil {
role.RoleCode = *req.RoleCode
}
if req.Description != nil {
role.Description = *req.Description
}
if req.Status != nil {
role.Status = *req.Status
}
if req.Sort != nil {
role.Sort = *req.Sort
}
// 使用事务更新角色和关联菜单
err = l.svcCtx.AdminRoleModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 更新角色
_, err = l.svcCtx.AdminRoleModel.Update(ctx, session, role)
if err != nil {
return err
}
if req.MenuIds != nil {
// 1. 获取当前关联的菜单ID
builder := l.svcCtx.AdminRoleMenuModel.SelectBuilder().
Where("role_id = ?", req.Id)
currentMenus, err := l.svcCtx.AdminRoleMenuModel.FindAll(ctx, builder, "id ASC")
if err != nil {
return err
}
// 2. 转换为map便于查找
currentMenuMap := make(map[int64]*model.AdminRoleMenu)
for _, menu := range currentMenus {
currentMenuMap[menu.MenuId] = menu
}
// 3. 检查新的菜单ID是否存在
for _, menuId := range req.MenuIds {
exists, err := l.svcCtx.AdminMenuModel.FindOne(ctx, menuId)
if err != nil || exists == nil {
return errors.Wrapf(xerr.NewErrMsg("菜单不存在"), "菜单ID: %d", menuId)
}
}
// 4. 找出需要删除和新增的关联
var toDelete []*model.AdminRoleMenu
var toInsert []int64
// 需要删除的:当前存在但新列表中没有的
for menuId, roleMenu := range currentMenuMap {
if !lo.Contains(req.MenuIds, menuId) {
toDelete = append(toDelete, roleMenu)
}
}
// 需要新增的:新列表中有但当前不存在的
for _, menuId := range req.MenuIds {
if _, exists := currentMenuMap[menuId]; !exists {
toInsert = append(toInsert, menuId)
}
}
// 5. 删除需要移除的关联
for _, roleMenu := range toDelete {
err = l.svcCtx.AdminRoleMenuModel.Delete(ctx, session, roleMenu.Id)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "删除角色菜单关联失败: %v", err)
}
}
// 6. 添加新的关联
for _, menuId := range toInsert {
roleMenu := &model.AdminRoleMenu{
RoleId: req.Id,
MenuId: menuId,
}
_, err = l.svcCtx.AdminRoleMenuModel.Insert(ctx, session, roleMenu)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "添加角色菜单关联失败: %v", err)
}
}
}
return nil
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新角色失败err: %v", err)
}
return &types.UpdateRoleResp{
Success: true,
}, nil
}

View File

@@ -0,0 +1,88 @@
package admin_user
import (
"context"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/xerr"
"tyc-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type AdminCreateUserLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminCreateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminCreateUserLogic {
return &AdminCreateUserLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminCreateUserLogic) AdminCreateUser(req *types.AdminCreateUserReq) (resp *types.AdminCreateUserResp, err error) {
// 检查用户名是否已存在
exists, err := l.svcCtx.AdminUserModel.FindOneByUsername(l.ctx, req.Username)
if err != nil && err != model.ErrNotFound {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户失败: %v", err)
}
if exists != nil {
return nil, errors.Wrapf(xerr.NewErrMsg("用户名已存在"), "创建用户失败")
}
password, err := crypto.PasswordHash("123456")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建用户失败, 加密密码失败: %v", err)
}
// 创建用户
user := &model.AdminUser{
Username: req.Username,
Password: password, // 注意:实际应用中需要加密密码
RealName: req.RealName,
Status: req.Status,
}
// 使用事务创建用户和关联角色
err = l.svcCtx.AdminUserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 创建用户
result, err := l.svcCtx.AdminUserModel.Insert(ctx, session, user)
if err != nil {
return err
}
userId, err := result.LastInsertId()
if err != nil {
return err
}
// 创建用户角色关联
if len(req.RoleIds) > 0 {
for _, roleId := range req.RoleIds {
userRole := &model.AdminUserRole{
UserId: userId,
RoleId: roleId,
}
_, err = l.svcCtx.AdminUserRoleModel.Insert(ctx, session, userRole)
if err != nil {
return err
}
}
}
return nil
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户失败: %v", err)
}
return &types.AdminCreateUserResp{
Id: user.Id,
}, nil
}

View File

@@ -0,0 +1,68 @@
package admin_user
import (
"context"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type AdminDeleteUserLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminDeleteUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminDeleteUserLogic {
return &AdminDeleteUserLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminDeleteUserLogic) AdminDeleteUser(req *types.AdminDeleteUserReq) (resp *types.AdminDeleteUserResp, err error) {
// 检查用户是否存在
_, err = l.svcCtx.AdminUserModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户不存在: %v", err)
}
// 使用事务删除用户和关联数据
err = l.svcCtx.AdminUserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 删除用户角色关联
builder := l.svcCtx.AdminUserRoleModel.SelectBuilder().
Where("user_id = ?", req.Id)
roles, err := l.svcCtx.AdminUserRoleModel.FindAll(ctx, builder, "id ASC")
if err != nil {
return err
}
for _, role := range roles {
err = l.svcCtx.AdminUserRoleModel.Delete(ctx, session, role.Id)
if err != nil {
return err
}
}
// 删除用户
err = l.svcCtx.AdminUserModel.Delete(ctx, session, req.Id)
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "删除用户失败: %v", err)
}
return &types.AdminDeleteUserResp{
Success: true,
}, nil
}

View File

@@ -0,0 +1,88 @@
package admin_user
import (
"context"
"errors"
"sync"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"github.com/samber/lo"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mr"
)
type AdminGetUserDetailLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetUserDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetUserDetailLogic {
return &AdminGetUserDetailLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetUserDetailLogic) AdminGetUserDetail(req *types.AdminGetUserDetailReq) (resp *types.AdminGetUserDetailResp, err error) {
// 使用MapReduceVoid并发获取用户信息和角色ID
var user *model.AdminUser
var roleIds []int64
var mutex sync.Mutex
var wg sync.WaitGroup
mr.MapReduceVoid(func(source chan<- interface{}) {
source <- 1 // 获取用户信息
source <- 2 // 获取角色ID
}, func(item interface{}, writer mr.Writer[interface{}], cancel func(error)) {
taskType := item.(int)
wg.Add(1)
defer wg.Done()
if taskType == 1 {
result, err := l.svcCtx.AdminUserModel.FindOne(l.ctx, req.Id)
if err != nil {
cancel(err)
return
}
mutex.Lock()
user = result
mutex.Unlock()
} else if taskType == 2 {
builder := l.svcCtx.AdminUserRoleModel.SelectBuilder().
Where("user_id = ?", req.Id)
roles, err := l.svcCtx.AdminUserRoleModel.FindAll(l.ctx, builder, "id ASC")
if err != nil {
cancel(err)
return
}
mutex.Lock()
roleIds = lo.Map(roles, func(item *model.AdminUserRole, _ int) int64 {
return item.RoleId
})
mutex.Unlock()
}
}, func(pipe <-chan interface{}, cancel func(error)) {
// 不需要处理pipe中的数据
})
wg.Wait()
if user == nil {
return nil, errors.New("用户不存在")
}
return &types.AdminGetUserDetailResp{
Id: user.Id,
Username: user.Username,
RealName: user.RealName,
Status: user.Status,
CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: user.UpdateTime.Format("2006-01-02 15:04:05"),
RoleIds: roleIds,
}, nil
}

View File

@@ -0,0 +1,149 @@
package admin_user
import (
"context"
"sync"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"github.com/samber/lo"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mr"
)
type AdminGetUserListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetUserListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetUserListLogic {
return &AdminGetUserListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetUserListLogic) AdminGetUserList(req *types.AdminGetUserListReq) (resp *types.AdminGetUserListResp, err error) {
resp = &types.AdminGetUserListResp{
Items: make([]types.UserListItem, 0),
Total: 0,
}
// 构建查询条件
builder := l.svcCtx.AdminUserModel.SelectBuilder().
Where("del_state = ?", 0)
if len(req.Username) > 0 {
builder = builder.Where("username LIKE ?", "%"+req.Username+"%")
}
if len(req.RealName) > 0 {
builder = builder.Where("real_name LIKE ?", "%"+req.RealName+"%")
}
if req.Status != -1 {
builder = builder.Where("status = ?", req.Status)
}
// 设置分页
offset := (req.Page - 1) * req.PageSize
builder = builder.OrderBy("id DESC").Limit(uint64(req.PageSize)).Offset(uint64(offset))
// 使用MapReduceVoid并发获取总数和列表数据
var users []*model.AdminUser
var total int64
var mutex sync.Mutex
var wg sync.WaitGroup
mr.MapReduceVoid(func(source chan<- interface{}) {
source <- 1 // 获取用户列表
source <- 2 // 获取总数
}, func(item interface{}, writer mr.Writer[*model.AdminUser], cancel func(error)) {
taskType := item.(int)
wg.Add(1)
defer wg.Done()
if taskType == 1 {
result, err := l.svcCtx.AdminUserModel.FindAll(l.ctx, builder, "id DESC")
if err != nil {
cancel(err)
return
}
mutex.Lock()
users = result
mutex.Unlock()
} else if taskType == 2 {
countBuilder := l.svcCtx.AdminUserModel.SelectBuilder().
Where("del_state = ?", 0)
if len(req.Username) > 0 {
countBuilder = countBuilder.Where("username LIKE ?", "%"+req.Username+"%")
}
if len(req.RealName) > 0 {
countBuilder = countBuilder.Where("real_name LIKE ?", "%"+req.RealName+"%")
}
if req.Status != -1 {
countBuilder = countBuilder.Where("status = ?", req.Status)
}
count, err := l.svcCtx.AdminUserModel.FindCount(l.ctx, countBuilder, "id")
if err != nil {
cancel(err)
return
}
mutex.Lock()
total = count
mutex.Unlock()
}
}, func(pipe <-chan *model.AdminUser, cancel func(error)) {
// 不需要处理pipe中的数据
})
wg.Wait()
// 并发获取每个用户的角色ID
var userItems []types.UserListItem
var userItemsMutex sync.Mutex
mr.MapReduceVoid(func(source chan<- interface{}) {
for _, user := range users {
source <- user
}
}, func(item interface{}, writer mr.Writer[[]int64], cancel func(error)) {
user := item.(*model.AdminUser)
// 获取用户关联的角色ID
builder := l.svcCtx.AdminUserRoleModel.SelectBuilder().
Where("user_id = ?", user.Id)
roles, err := l.svcCtx.AdminUserRoleModel.FindAll(l.ctx, builder, "id ASC")
if err != nil {
cancel(err)
return
}
roleIds := lo.Map(roles, func(item *model.AdminUserRole, _ int) int64 {
return item.RoleId
})
writer.Write(roleIds)
}, func(pipe <-chan []int64, cancel func(error)) {
for _, user := range users {
roleIds := <-pipe
item := types.UserListItem{
Id: user.Id,
Username: user.Username,
RealName: user.RealName,
Status: user.Status,
CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"),
RoleIds: roleIds,
}
userItemsMutex.Lock()
userItems = append(userItems, item)
userItemsMutex.Unlock()
}
})
resp.Items = userItems
resp.Total = total
return resp, nil
}

View File

@@ -0,0 +1,141 @@
package admin_user
import (
"context"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/app/main/model"
"tyc-server/common/xerr"
"github.com/pkg/errors"
"github.com/samber/lo"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type AdminUpdateUserLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateUserLogic {
return &AdminUpdateUserLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateUserLogic) AdminUpdateUser(req *types.AdminUpdateUserReq) (resp *types.AdminUpdateUserResp, err error) {
// 检查用户是否存在
user, err := l.svcCtx.AdminUserModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户不存在: %v", err)
}
// 检查用户名是否重复
if req.Username != nil && *req.Username != user.Username {
exists, err := l.svcCtx.AdminUserModel.FindOneByUsername(l.ctx, *req.Username)
if err != nil && err != model.ErrNotFound {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新用户失败: %v", err)
}
if exists != nil {
return nil, errors.Wrapf(xerr.NewErrMsg("用户名已存在"), "更新用户失败")
}
}
// 更新用户信息
if req.Username != nil {
user.Username = *req.Username
}
if req.RealName != nil {
user.RealName = *req.RealName
}
if req.Status != nil {
user.Status = *req.Status
}
// 使用事务更新用户和关联角色
err = l.svcCtx.AdminUserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 更新用户
_, err = l.svcCtx.AdminUserModel.Update(ctx, session, user)
if err != nil {
return err
}
// 只有当RoleIds不为nil时才更新角色关联
if req.RoleIds != nil {
// 1. 获取当前关联的角色ID
builder := l.svcCtx.AdminUserRoleModel.SelectBuilder().
Where("user_id = ?", req.Id)
currentRoles, err := l.svcCtx.AdminUserRoleModel.FindAll(ctx, builder, "id ASC")
if err != nil {
return err
}
// 2. 转换为map便于查找
currentRoleMap := make(map[int64]*model.AdminUserRole)
for _, role := range currentRoles {
currentRoleMap[role.RoleId] = role
}
// 3. 检查新的角色ID是否存在
for _, roleId := range req.RoleIds {
exists, err := l.svcCtx.AdminRoleModel.FindOne(ctx, roleId)
if err != nil || exists == nil {
return errors.Wrapf(xerr.NewErrMsg("角色不存在"), "角色ID: %d", roleId)
}
}
// 4. 找出需要删除和新增的关联
var toDelete []*model.AdminUserRole
var toInsert []int64
// 需要删除的:当前存在但新列表中没有的
for roleId, userRole := range currentRoleMap {
if !lo.Contains(req.RoleIds, roleId) {
toDelete = append(toDelete, userRole)
}
}
// 需要新增的:新列表中有但当前不存在的
for _, roleId := range req.RoleIds {
if _, exists := currentRoleMap[roleId]; !exists {
toInsert = append(toInsert, roleId)
}
}
// 5. 删除需要移除的关联
for _, userRole := range toDelete {
err = l.svcCtx.AdminUserRoleModel.Delete(ctx, session, userRole.Id)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "删除用户角色关联失败: %v", err)
}
}
// 6. 添加新的关联
for _, roleId := range toInsert {
userRole := &model.AdminUserRole{
UserId: req.Id,
RoleId: roleId,
}
_, err = l.svcCtx.AdminUserRoleModel.Insert(ctx, session, userRole)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "添加用户角色关联失败: %v", err)
}
}
}
return nil
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新用户失败: %v", err)
}
return &types.AdminUpdateUserResp{
Success: true,
}, nil
}

View File

@@ -0,0 +1,67 @@
package admin_user
import (
"context"
"tyc-server/app/main/api/internal/svc"
"tyc-server/app/main/api/internal/types"
"tyc-server/common/ctxdata"
"tyc-server/common/xerr"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminUserInfoLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUserInfoLogic {
return &AdminUserInfoLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUserInfoLogic) AdminUserInfo(req *types.AdminUserInfoReq) (resp *types.AdminUserInfoResp, err error) {
userId, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败, %+v", err)
}
user, err := l.svcCtx.AdminUserModel.FindOne(l.ctx, userId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID信息失败, %+v", err)
}
// 获取权限
adminUserRoleBuilder := l.svcCtx.AdminUserRoleModel.SelectBuilder().Where(squirrel.Eq{"user_id": user.Id})
permissions, err := l.svcCtx.AdminUserRoleModel.FindAll(l.ctx, adminUserRoleBuilder, "role_id DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrMsg("获取权限失败"), "用户登录, 获取权限失败, 用户名: %s", user.Username)
}
// 获取角色ID数组
roleIds := make([]int64, 0)
for _, permission := range permissions {
roleIds = append(roleIds, permission.RoleId)
}
// 获取角色名称
roles := make([]string, 0)
for _, roleId := range roleIds {
role, err := l.svcCtx.AdminRoleModel.FindOne(l.ctx, roleId)
if err != nil {
continue
}
roles = append(roles, role.RoleCode)
}
return &types.AdminUserInfoResp{
Username: user.Username,
RealName: user.RealName,
Roles: roles,
}, nil
}

View File

@@ -99,6 +99,29 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取保存订单ID失败: %+v", lastInsertIdErr)
}
orderID = insertedOrderID
promoteKey, ok := l.ctx.Value("promoteKey").(string)
if ok && promoteKey != "" {
// TODO: 在这里添加处理 promoteKey 的逻辑
url := fmt.Sprintf("%s/%s", l.svcCtx.Config.AdminPromotion.URLDomain, promoteKey)
promoteLink, err := l.svcCtx.AdminPromotionLinkModel.FindOneByUrl(l.ctx, url)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 查找推广链接失败: %+v", err)
}
if promoteLink != nil {
promoteOrder := &model.AdminPromotionOrder{
OrderId: orderID,
LinkId: promoteLink.Id,
UserId: userID,
AdminUserId: promoteLink.AdminUserId,
}
_, insertPromoteOrderErr := l.svcCtx.AdminPromotionOrderModel.Insert(ctx, session, promoteOrder)
if insertPromoteOrderErr != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 保存推广订单失败: %+v", insertPromoteOrderErr)
}
}
}
return nil
})
if transErr != nil {

View File

@@ -0,0 +1,43 @@
package middleware
import (
"net/http"
"tyc-server/app/main/api/internal/config"
)
type AuthInterceptorMiddleware struct {
Config config.Config
}
func NewAuthInterceptorMiddleware(c config.Config) *AuthInterceptorMiddleware {
return &AuthInterceptorMiddleware{
Config: c,
}
}
func (m *AuthInterceptorMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 从请求头中获取Authorization字段
// authHeader := r.Header.Get("Authorization")
// if authHeader != "" && strings.HasPrefix(authHeader, "Bearer ") {
// // 提取JWT令牌去除Bearer前缀
// tokenString := strings.TrimPrefix(authHeader, "Bearer ")
// // 使用jwtx.ParseJwtTokenV2()解析令牌
// claims, err := jwtx.ParseJwtTokenV2(tokenString, m.Config.AdminConfig.AccessSecret)
// if err != nil {
// resultErr := errors.Wrapf(xerr.NewErrMsg("用户名或密码错误"), "用户登录, 用户名或密码错误, 用户名: %s")
// result.HttpResult(r, w, nil, resultErr)
// return
// }
// // 如果解析失败可以添加处理代码如返回401错误
// }
// 如果没有Authorization头或解析失败继续传递到下一个处理器
// 根据业务需求可以决定是否继续处理请求
next(w, r)
}
}

View File

@@ -0,0 +1,34 @@
package middleware
import (
"context"
"net/http"
)
type contextKey string
const (
brandKey contextKey = "brand"
platformKey contextKey = "platform"
promoteKey contextKey = "promoteKey"
)
func ReqHeaderCtxMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
brand := r.Header.Get("X-Brand")
platform := r.Header.Get("X-Platform")
promoteValue := r.Header.Get("X-Promote-Key")
ctx := r.Context()
if brand != "" {
ctx = context.WithValue(ctx, brandKey, brand)
}
if platform != "" {
ctx = context.WithValue(ctx, platformKey, platform)
}
if promoteValue != "" {
ctx = context.WithValue(ctx, promoteKey, promoteValue)
}
r = r.WithContext(ctx)
next(w, r)
}
}

View File

@@ -0,0 +1,47 @@
package service
import (
"context"
"errors"
"tyc-server/app/main/model"
)
type DictService struct {
adminDictTypeModel model.AdminDictTypeModel
adminDictDataModel model.AdminDictDataModel
}
func NewDictService(adminDictTypeModel model.AdminDictTypeModel, adminDictDataModel model.AdminDictDataModel) *DictService {
return &DictService{adminDictTypeModel: adminDictTypeModel, adminDictDataModel: adminDictDataModel}
}
func (s *DictService) GetDictLabel(ctx context.Context, dictType string, dictValue int64) (string, error) {
dictTypeModel, err := s.adminDictTypeModel.FindOneByDictType(ctx, dictType)
if err != nil {
return "", err
}
if dictTypeModel.Status != 1 {
return "", errors.New("字典类型未启用")
}
dictData, err := s.adminDictDataModel.FindOneByDictTypeDictValue(ctx, dictTypeModel.DictType, dictValue)
if err != nil {
return "", err
}
if dictData.Status != 1 {
return "", errors.New("字典数据未启用")
}
return dictData.DictLabel, nil
}
func (s *DictService) GetDictValue(ctx context.Context, dictType string, dictLabel string) (int64, error) {
dictTypeModel, err := s.adminDictTypeModel.FindOneByDictType(ctx, dictType)
if err != nil {
return 0, err
}
if dictTypeModel.Status != 1 {
return 0, errors.New("字典类型未启用")
}
dictData, err := s.adminDictDataModel.FindOneByDictTypeDictLabel(ctx, dictTypeModel.DictType, dictLabel)
if err != nil {
return 0, err
}
return dictData.DictValue, nil
}

View File

@@ -14,9 +14,14 @@ import (
)
type ServiceContext struct {
Config config.Config
Redis *redis.Redis
SourceInterceptor rest.Middleware
Config config.Config
Redis *redis.Redis
// 中间件
SourceInterceptor rest.Middleware
AuthInterceptor rest.Middleware
// 模型
UserModel model.UserModel
UserAuthModel model.UserAuthModel
ProductModel model.ProductModel
@@ -28,16 +33,36 @@ type ServiceContext struct {
GlobalNotificationsModel model.GlobalNotificationsModel
ExampleModel model.ExampleModel
ExampleParamsModel model.ExampleParamsModel
AlipayService *service.AliPayService
WechatPayService *service.WechatPayService
ApplePayService *service.ApplePayService
WestDexService *service.WestDexService
YushanService *service.YushanService
TianjuService *service.TianjuService
ApiRequestService *service.ApiRequestService
AsynqServer *asynq.Server // 服务端
AsynqService *service.AsynqService // 客户端
VerificationService *service.VerificationService
// 服务
AlipayService *service.AliPayService
WechatPayService *service.WechatPayService
ApplePayService *service.ApplePayService
WestDexService *service.WestDexService
YushanService *service.YushanService
TianjuService *service.TianjuService
ApiRequestService *service.ApiRequestService
AsynqServer *asynq.Server // 服务端
AsynqService *service.AsynqService // 客户端
VerificationService *service.VerificationService
DictService *service.DictService
// admin
AdminApiModel model.AdminApiModel
AdminMenuModel model.AdminMenuModel
AdminRoleModel model.AdminRoleModel
AdminRoleApiModel model.AdminRoleApiModel
AdminRoleMenuModel model.AdminRoleMenuModel
AdminUserModel model.AdminUserModel
AdminUserRoleModel model.AdminUserRoleModel
AdminDictDataModel model.AdminDictDataModel
AdminDictTypeModel model.AdminDictTypeModel
// admin promotion
AdminPromotionLinkModel model.AdminPromotionLinkModel
AdminPromotionLinkStatsTotalModel model.AdminPromotionLinkStatsTotalModel
AdminPromotionLinkStatsHistoryModel model.AdminPromotionLinkStatsHistoryModel
AdminPromotionOrderModel model.AdminPromotionOrderModel
}
func NewServiceContext(c config.Config) *ServiceContext {
@@ -57,6 +82,22 @@ func NewServiceContext(c config.Config) *ServiceContext {
Concurrency: 10,
},
)
adminApiModel := model.NewAdminApiModel(db, c.CacheRedis)
adminMenuModel := model.NewAdminMenuModel(db, c.CacheRedis)
adminRoleModel := model.NewAdminRoleModel(db, c.CacheRedis)
adminRoleApiModel := model.NewAdminRoleApiModel(db, c.CacheRedis)
adminRoleMenuModel := model.NewAdminRoleMenuModel(db, c.CacheRedis)
adminUserModel := model.NewAdminUserModel(db, c.CacheRedis)
adminUserRoleModel := model.NewAdminUserRoleModel(db, c.CacheRedis)
adminDictDataModel := model.NewAdminDictDataModel(db, c.CacheRedis)
adminDictTypeModel := model.NewAdminDictTypeModel(db, c.CacheRedis)
// admin promotion
adminPromotionLinkModel := model.NewAdminPromotionLinkModel(db, c.CacheRedis)
adminPromotionLinkStatsTotalModel := model.NewAdminPromotionLinkStatsTotalModel(db, c.CacheRedis)
adminPromotionLinkStatsHistoryModel := model.NewAdminPromotionLinkStatsHistoryModel(db, c.CacheRedis)
adminPromotionOrderModel := model.NewAdminPromotionOrderModel(db, c.CacheRedis)
westDexService := service.NewWestDexService(c)
yushanService := service.NewYushanService(c)
tianjuService := service.NewTianjuService(c)
@@ -65,10 +106,12 @@ func NewServiceContext(c config.Config) *ServiceContext {
userAuthModel := model.NewUserAuthModel(db, c.CacheRedis)
apiRequestService := service.NewApiRequestService(c, westDexService, yushanService, tianjuService, featureModel, productFeatureModel)
dictService := service.NewDictService(adminDictTypeModel, adminDictDataModel)
return &ServiceContext{
Config: c,
Redis: redis.MustNewRedis(redisConf),
SourceInterceptor: middleware.NewSourceInterceptorMiddleware().Handle,
AuthInterceptor: middleware.NewAuthInterceptorMiddleware(c).Handle,
AlipayService: service.NewAliPayService(c),
WechatPayService: service.NewWechatPayService(c, userAuthModel),
ApplePayService: service.NewApplePayService(c),
@@ -79,6 +122,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
AsynqServer: asynqServer,
ApiRequestService: apiRequestService,
AsynqService: service.NewAsynqService(c),
DictService: dictService,
UserModel: model.NewUserModel(db, c.CacheRedis),
UserAuthModel: userAuthModel,
ProductModel: model.NewProductModel(db, c.CacheRedis),
@@ -90,6 +134,21 @@ func NewServiceContext(c config.Config) *ServiceContext {
GlobalNotificationsModel: model.NewGlobalNotificationsModel(db, c.CacheRedis),
FeatureModel: featureModel,
ProductFeatureModel: productFeatureModel,
AdminApiModel: adminApiModel,
AdminMenuModel: adminMenuModel,
AdminRoleModel: adminRoleModel,
AdminRoleApiModel: adminRoleApiModel,
AdminRoleMenuModel: adminRoleMenuModel,
AdminUserModel: adminUserModel,
AdminUserRoleModel: adminUserRoleModel,
AdminDictDataModel: adminDictDataModel,
AdminDictTypeModel: adminDictTypeModel,
AdminPromotionLinkModel: adminPromotionLinkModel,
AdminPromotionLinkStatsTotalModel: adminPromotionLinkStatsTotalModel,
AdminPromotionLinkStatsHistoryModel: adminPromotionLinkStatsHistoryModel,
AdminPromotionOrderModel: adminPromotionOrderModel,
}
}
func (s *ServiceContext) Close() {

View File

@@ -1,12 +1,193 @@
// Code generated by goctl. DO NOT EDIT.
package types
type AdminCreateUserReq struct {
Username string `json:"username"` // 用户名
RealName string `json:"real_name"` // 真实姓名
Status int64 `json:"status,default=1"` // 状态0-禁用1-启用
RoleIds []int64 `json:"role_ids"` // 关联的角色ID列表
}
type AdminCreateUserResp struct {
Id int64 `json:"id"` // 用户ID
}
type AdminDeleteUserReq struct {
Id int64 `path:"id"` // 用户ID
}
type AdminDeleteUserResp struct {
Success bool `json:"success"` // 是否成功
}
type AdminGetUserDetailReq struct {
Id int64 `path:"id"` // 用户ID
}
type AdminGetUserDetailResp struct {
Id int64 `json:"id"` // 用户ID
Username string `json:"username"` // 用户名
RealName string `json:"real_name"` // 真实姓名
Status int64 `json:"status"` // 状态0-禁用1-启用
CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
RoleIds []int64 `json:"role_ids"` // 关联的角色ID列表
}
type AdminGetUserListReq struct {
Page int64 `form:"page,default=1"` // 页码
PageSize int64 `form:"pageSize,default=20"` // 每页数量
Username string `form:"username,optional"` // 用户名
RealName string `form:"real_name,optional"` // 真实姓名
Status int64 `form:"status,optional,default=-1"` // 状态0-禁用1-启用
}
type AdminGetUserListResp struct {
Total int64 `json:"total"` // 总数
Items []UserListItem `json:"items"` // 列表
}
type AdminLoginReq struct {
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Captcha bool `json:"captcha" validate:"required"`
}
type AdminLoginResp struct {
AccessToken string `json:"access_token"`
AccessExpire int64 `json:"access_expire"`
RefreshAfter int64 `json:"refresh_after"`
Roles []string `json:"roles"`
}
type AdminUpdateUserReq struct {
Id int64 `path:"id"` // 用户ID
Username *string `json:"username,optional"` // 用户名
RealName *string `json:"real_name,optional"` // 真实姓名
Status *int64 `json:"status,optional"` // 状态0-禁用1-启用
RoleIds []int64 `json:"role_ids,optional"` // 关联的角色ID列表
}
type AdminUpdateUserResp struct {
Success bool `json:"success"` // 是否成功
}
type AdminUserInfoReq struct {
}
type AdminUserInfoResp struct {
Username string `json:"username"` // 用户名
RealName string `json:"real_name"` // 真实姓名
Roles []string `json:"roles"` // 角色编码列表
}
type CreateMenuReq struct {
Pid int64 `json:"pid,optional"` // 父菜单ID
Name string `json:"name"` // 路由名称
Path string `json:"path,optional"` // 路由路径
Component string `json:"component,optional"` // 组件路径
Redirect string `json:"redirect,optional"` // 重定向路径
Meta map[string]interface{} `json:"meta"` // 路由元数据
Status int64 `json:"status,optional,default=1"` // 状态0-禁用1-启用
Type string `json:"type"` // 类型
Sort int64 `json:"sort,optional"` // 排序
}
type CreateMenuResp struct {
Id int64 `json:"id"` // 菜单ID
}
type CreatePromotionLinkReq struct {
Name string `json:"name"` // 链接名称
}
type CreatePromotionLinkResp struct {
Id int64 `json:"id"` // 链接ID
Url string `json:"url"` // 生成的推广链接URL
}
type CreateRoleReq struct {
RoleName string `json:"role_name"` // 角色名称
RoleCode string `json:"role_code"` // 角色编码
Description string `json:"description"` // 角色描述
Status int64 `json:"status,default=1"` // 状态0-禁用1-启用
Sort int64 `json:"sort,default=0"` // 排序
MenuIds []int64 `json:"menu_ids"` // 关联的菜单ID列表
}
type CreateRoleResp struct {
Id int64 `json:"id"` // 角色ID
}
type DeleteMenuReq struct {
Id int64 `path:"id"` // 菜单ID
}
type DeleteMenuResp struct {
Success bool `json:"success"` // 是否成功
}
type DeletePromotionLinkReq struct {
Id int64 `path:"id"` // 链接ID
}
type DeletePromotionLinkResp struct {
Success bool `json:"success"` // 是否成功
}
type DeleteRoleReq struct {
Id int64 `path:"id"` // 角色ID
}
type DeleteRoleResp struct {
Success bool `json:"success"` // 是否成功
}
type Feature struct {
ID int64 `json:"id"` // 功能ID
ApiID string `json:"api_id"` // API标识
Name string `json:"name"` // 功能描述
}
type GetMenuAllReq struct {
}
type GetMenuAllResp struct {
Name string `json:"name"`
Path string `json:"path"`
Redirect string `json:"redirect,omitempty"`
Component string `json:"component,omitempty"`
Sort int64 `json:"sort"`
Meta map[string]interface{} `json:"meta"`
Children []GetMenuAllResp `json:"children"`
}
type GetMenuDetailReq struct {
Id int64 `path:"id"` // 菜单ID
}
type GetMenuDetailResp struct {
Id int64 `json:"id"` // 菜单ID
Pid int64 `json:"pid"` // 父菜单ID
Name string `json:"name"` // 路由名称
Path string `json:"path"` // 路由路径
Component string `json:"component"` // 组件路径
Redirect string `json:"redirect"` // 重定向路径
Meta map[string]interface{} `json:"meta"` // 路由元数据
Status int64 `json:"status"` // 状态0-禁用1-启用
Type string `json:"type"` // 类型
Sort int64 `json:"sort"` // 排序
CreateTime string `json:"createTime"` // 创建时间
UpdateTime string `json:"updateTime"` // 更新时间
}
type GetMenuListReq struct {
Name string `form:"name,optional"` // 菜单名称
Path string `form:"path,optional"` // 路由路径
Status int64 `form:"status,optional,default=-1"` // 状态0-禁用1-启用
Type string `form:"type,optional"` // 类型
}
type GetNotificationsResp struct {
Notifications []Notification `json:"notifications"` // 通知列表
Total int64 `json:"total"` // 总记录数
@@ -28,11 +209,100 @@ type GetProductRenderListResponse struct {
Product []Product
}
type GetPromotionLinkDetailReq struct {
Id int64 `path:"id"` // 链接ID
}
type GetPromotionLinkDetailResp struct {
Name string `json:"name"` // 链接名称
Url string `json:"url"` // 推广链接URL
ClickCount int64 `json:"click_count"` // 点击数
PayCount int64 `json:"pay_count"` // 付费次数
PayAmount string `json:"pay_amount"` // 付费金额
CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
LastClickTime string `json:"last_click_time,optional"` // 最后点击时间
LastPayTime string `json:"last_pay_time,optional"` // 最后付费时间
}
type GetPromotionLinkListReq struct {
Page int64 `form:"page,default=1"` // 页码
PageSize int64 `form:"pageSize,default=20"` // 每页数量
Name string `form:"name,optional"` // 链接名称
Url string `form:"url,optional"` // 推广链接URL
}
type GetPromotionLinkListResp struct {
Total int64 `json:"total"` // 总数
Items []PromotionLinkItem `json:"items"` // 列表
}
type GetPromotionStatsHistoryReq struct {
StartDate string `form:"start_date"` // 开始日期格式YYYY-MM-DD
EndDate string `form:"end_date"` // 结束日期格式YYYY-MM-DD
}
type GetPromotionStatsTotalReq struct {
}
type GetPromotionStatsTotalResp struct {
TodayPayAmount float64 `json:"today_pay_amount"` // 今日金额
TodayClickCount int64 `json:"today_click_count"` // 今日点击数
TodayPayCount int64 `json:"today_pay_count"` // 今日付费次数
TotalPayAmount float64 `json:"total_pay_amount"` // 总金额
TotalClickCount int64 `json:"total_click_count"` // 总点击数
TotalPayCount int64 `json:"total_pay_count"` // 总付费次数
}
type GetRoleDetailReq struct {
Id int64 `path:"id"` // 角色ID
}
type GetRoleDetailResp struct {
Id int64 `json:"id"` // 角色ID
RoleName string `json:"role_name"` // 角色名称
RoleCode string `json:"role_code"` // 角色编码
Description string `json:"description"` // 角色描述
Status int64 `json:"status"` // 状态0-禁用1-启用
Sort int64 `json:"sort"` // 排序
CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
MenuIds []int64 `json:"menu_ids"` // 关联的菜单ID列表
}
type GetRoleListReq struct {
Page int64 `form:"page,default=1"` // 页码
PageSize int64 `form:"pageSize,default=20"` // 每页数量
Name string `form:"name,optional"` // 角色名称
Code string `form:"code,optional"` // 角色编码
Status int64 `form:"status,optional,default=-1"` // 状态0-禁用1-启用
}
type GetRoleListResp struct {
Total int64 `json:"total"` // 总数
Items []RoleListItem `json:"items"` // 列表
}
type IapCallbackReq struct {
OrderID int64 `json:"order_id" validate:"required"`
TransactionReceipt string `json:"transaction_receipt" validate:"required"`
}
type MenuListItem struct {
Id int64 `json:"id"` // 菜单ID
Pid int64 `json:"pid"` // 父菜单ID
Name string `json:"name"` // 路由名称
Path string `json:"path"` // 路由路径
Component string `json:"component"` // 组件路径
Redirect string `json:"redirect"` // 重定向路径
Meta map[string]interface{} `json:"meta"` // 路由元数据
Status int64 `json:"status"` // 状态0-禁用1-启用
Type string `json:"type"` // 类型
Sort int64 `json:"sort"` // 排序
CreateTime string `json:"createTime"` // 创建时间
Children []MenuListItem `json:"children"` // 子菜单
}
type MobileCodeLoginReq struct {
Mobile string `json:"mobile"`
Code string `json:"code" validate:"required"`
@@ -89,6 +359,27 @@ type ProductResponse struct {
Product
}
type PromotionLinkItem struct {
Id int64 `json:"id"` // 链接ID
Name string `json:"name"` // 链接名称
Url string `json:"url"` // 推广链接URL
ClickCount int64 `json:"click_count"` // 点击数
PayCount int64 `json:"pay_count"` // 付费次数
PayAmount string `json:"pay_amount"` // 付费金额
CreateTime string `json:"create_time"` // 创建时间
LastClickTime string `json:"last_click_time,optional"` // 最后点击时间
LastPayTime string `json:"last_pay_time,optional"` // 最后付费时间
}
type PromotionStatsHistoryItem struct {
Id int64 `json:"id"` // 记录ID
LinkId int64 `json:"link_id"` // 链接ID
PayAmount float64 `json:"pay_amount"` // 金额
ClickCount int64 `json:"click_count"` // 点击数
PayCount int64 `json:"pay_count"` // 付费次数
StatsDate string `json:"stats_date"` // 统计日期
}
type Query struct {
Id int64 `json:"id"` // 主键ID
OrderId int64 `json:"order_id"` // 订单ID
@@ -191,6 +482,14 @@ type QuerySingleTestResp struct {
Api string `json:"api"`
}
type RecordLinkClickReq struct {
Path string `path:"path"` // 链接路径
}
type RecordLinkClickResp struct {
Success bool `json:"success"` // 是否成功
}
type RegisterReq struct {
Mobile string `json:"mobile" validate:"required,mobile"`
Password string `json:"password" validate:"required,min=11,max=11,password"`
@@ -203,6 +502,43 @@ type RegisterResp struct {
RefreshAfter int64 `json:"refreshAfter"`
}
type RoleListItem struct {
Id int64 `json:"id"` // 角色ID
RoleName string `json:"role_name"` // 角色名称
RoleCode string `json:"role_code"` // 角色编码
Description string `json:"description"` // 角色描述
Status int64 `json:"status"` // 状态0-禁用1-启用
Sort int64 `json:"sort"` // 排序
CreateTime string `json:"create_time"` // 创建时间
MenuIds []int64 `json:"menu_ids"` // 关联的菜单ID列表
}
type UpdateMenuReq struct {
Id int64 `path:"id"` // 菜单ID
Pid int64 `json:"pid,optional"` // 父菜单ID
Name string `json:"name"` // 路由名称
Path string `json:"path,optional"` // 路由路径
Component string `json:"component,optional"` // 组件路径
Redirect string `json:"redirect,optional"` // 重定向路径
Meta map[string]interface{} `json:"meta"` // 路由元数据
Status int64 `json:"status,optional"` // 状态0-禁用1-启用
Type string `json:"type"` // 类型
Sort int64 `json:"sort,optional"` // 排序
}
type UpdateMenuResp struct {
Success bool `json:"success"` // 是否成功
}
type UpdatePromotionLinkReq struct {
Id int64 `path:"id"` // 链接ID
Name *string `json:"name,optional"` // 链接名称
}
type UpdatePromotionLinkResp struct {
Success bool `json:"success"` // 是否成功
}
type UpdateQueryDataReq struct {
Id int64 `json:"id"` // 查询ID
QueryData string `json:"query_data"` // 查询数据(未加密的JSON)
@@ -213,6 +549,20 @@ type UpdateQueryDataResp struct {
UpdatedAt string `json:"updated_at"` // 更新时间
}
type UpdateRoleReq struct {
Id int64 `path:"id"` // 角色ID
RoleName *string `json:"role_name,optional"` // 角色名称
RoleCode *string `json:"role_code,optional"` // 角色编码
Description *string `json:"description,optional"` // 角色描述
Status *int64 `json:"status,optional"` // 状态0-禁用1-启用
Sort *int64 `json:"sort,optional"` // 排序
MenuIds []int64 `json:"menu_ids,optional"` // 关联的菜单ID列表
}
type UpdateRoleResp struct {
Success bool `json:"success"` // 是否成功
}
type User struct {
Id int64 `json:"id"`
Mobile string `json:"mobile"`
@@ -223,6 +573,15 @@ type UserInfoResp struct {
UserInfo User `json:"userInfo"`
}
type UserListItem struct {
Id int64 `json:"id"` // 用户ID
Username string `json:"username"` // 用户名
RealName string `json:"real_name"` // 真实姓名
Status int64 `json:"status"` // 状态0-禁用1-启用
CreateTime string `json:"create_time"` // 创建时间
RoleIds []int64 `json:"role_ids"` // 关联的角色ID列表
}
type WXH5AuthReq struct {
Code string `json:"code"`
}