first commit
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetAgentCommissionDeductionListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetAgentCommissionDeductionListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentCommissionDeductionListLogic {
|
||||
return &AdminGetAgentCommissionDeductionListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetAgentCommissionDeductionListLogic) AdminGetAgentCommissionDeductionList(req *types.AdminGetAgentCommissionDeductionListReq) (resp *types.AdminGetAgentCommissionDeductionListResp, err error) {
|
||||
builder := l.svcCtx.AgentCommissionDeductionModel.SelectBuilder()
|
||||
if req.AgentId != nil {
|
||||
builder = builder.Where(squirrel.Eq{"agent_id": *req.AgentId})
|
||||
}
|
||||
if req.Type != nil && *req.Type != "" {
|
||||
builder = builder.Where(squirrel.Eq{"type": *req.Type})
|
||||
}
|
||||
if req.Status != nil {
|
||||
builder = builder.Where(squirrel.Eq{"status": *req.Status})
|
||||
}
|
||||
// 产品名筛选需先查product_id
|
||||
if req.ProductName != nil && *req.ProductName != "" {
|
||||
products, err := l.svcCtx.ProductModel.FindAll(l.ctx, l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"product_name": *req.ProductName}), "")
|
||||
if err != nil || len(products) == 0 {
|
||||
return &types.AdminGetAgentCommissionDeductionListResp{Total: 0, Items: []types.AgentCommissionDeductionListItem{}}, nil
|
||||
}
|
||||
builder = builder.Where("product_id = ?", products[0].Id)
|
||||
}
|
||||
|
||||
list, total, err := l.svcCtx.AgentCommissionDeductionModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 批量查product_id->name
|
||||
productIds := make(map[int64]struct{})
|
||||
for _, v := range list {
|
||||
productIds[v.ProductId] = struct{}{}
|
||||
}
|
||||
productIdArr := make([]int64, 0, len(productIds))
|
||||
for id := range productIds {
|
||||
productIdArr = append(productIdArr, id)
|
||||
}
|
||||
productNameMap := make(map[int64]string)
|
||||
if len(productIdArr) > 0 {
|
||||
build := l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"id": productIdArr})
|
||||
products, _ := l.svcCtx.ProductModel.FindAll(l.ctx, build, "")
|
||||
for _, p := range products {
|
||||
productNameMap[p.Id] = p.ProductName
|
||||
}
|
||||
}
|
||||
|
||||
items := make([]types.AgentCommissionDeductionListItem, 0, len(list))
|
||||
for _, v := range list {
|
||||
item := types.AgentCommissionDeductionListItem{}
|
||||
_ = copier.Copy(&item, v)
|
||||
item.ProductName = productNameMap[v.ProductId]
|
||||
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
|
||||
items = append(items, item)
|
||||
}
|
||||
resp = &types.AdminGetAgentCommissionDeductionListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetAgentCommissionListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetAgentCommissionListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentCommissionListLogic {
|
||||
return &AdminGetAgentCommissionListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetAgentCommissionListLogic) AdminGetAgentCommissionList(req *types.AdminGetAgentCommissionListReq) (resp *types.AdminGetAgentCommissionListResp, err error) {
|
||||
builder := l.svcCtx.AgentCommissionModel.SelectBuilder()
|
||||
if req.AgentId != nil {
|
||||
builder = builder.Where(squirrel.Eq{"agent_id": *req.AgentId})
|
||||
}
|
||||
if req.Status != nil {
|
||||
builder = builder.Where(squirrel.Eq{"status": *req.Status})
|
||||
}
|
||||
// 先查出所有product_id对应的product_name(如有product_name筛选,需反查id)
|
||||
var productIdFilter int64
|
||||
if req.ProductName != nil && *req.ProductName != "" {
|
||||
// 只支持精确匹配,如需模糊可扩展
|
||||
products, err := l.svcCtx.ProductModel.FindAll(l.ctx, l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"product_name": *req.ProductName}), "")
|
||||
if err != nil || len(products) == 0 {
|
||||
return &types.AdminGetAgentCommissionListResp{Total: 0, Items: []types.AgentCommissionListItem{}}, nil
|
||||
}
|
||||
productIdFilter = products[0].Id
|
||||
builder = builder.Where("product_id = ?", productIdFilter)
|
||||
}
|
||||
|
||||
list, total, err := l.svcCtx.AgentCommissionModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 批量查product_name
|
||||
productIds := make(map[int64]struct{})
|
||||
for _, v := range list {
|
||||
productIds[v.ProductId] = struct{}{}
|
||||
}
|
||||
productNameMap := make(map[int64]string)
|
||||
if len(productIds) > 0 {
|
||||
ids := make([]int64, 0, len(productIds))
|
||||
for id := range productIds {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
builder := l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"id": ids})
|
||||
products, _ := l.svcCtx.ProductModel.FindAll(l.ctx, builder, "")
|
||||
for _, p := range products {
|
||||
productNameMap[p.Id] = p.ProductName
|
||||
}
|
||||
}
|
||||
|
||||
items := make([]types.AgentCommissionListItem, 0, len(list))
|
||||
for _, v := range list {
|
||||
item := types.AgentCommissionListItem{}
|
||||
_ = copier.Copy(&item, v)
|
||||
item.ProductName = productNameMap[v.ProductId]
|
||||
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
|
||||
items = append(items, item)
|
||||
}
|
||||
resp = &types.AdminGetAgentCommissionListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetAgentLinkListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetAgentLinkListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentLinkListLogic {
|
||||
return &AdminGetAgentLinkListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetAgentLinkListLogic) AdminGetAgentLinkList(req *types.AdminGetAgentLinkListReq) (resp *types.AdminGetAgentLinkListResp, err error) {
|
||||
builder := l.svcCtx.AgentLinkModel.SelectBuilder()
|
||||
if req.AgentId != nil {
|
||||
builder = builder.Where("agent_id = ?", *req.AgentId)
|
||||
}
|
||||
if req.LinkIdentifier != nil && *req.LinkIdentifier != "" {
|
||||
builder = builder.Where("link_identifier = ?", *req.LinkIdentifier)
|
||||
}
|
||||
|
||||
// 先查出所有product_id对应的product_name(如有product_name筛选,需反查id)
|
||||
var productIdFilter int64
|
||||
if req.ProductName != nil && *req.ProductName != "" {
|
||||
// 只支持精确匹配,如需模糊可扩展
|
||||
products, err := l.svcCtx.ProductModel.FindAll(l.ctx, l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"product_name": *req.ProductName}), "")
|
||||
if err != nil || len(products) == 0 {
|
||||
return &types.AdminGetAgentLinkListResp{Total: 0, Items: []types.AgentLinkListItem{}}, nil
|
||||
}
|
||||
productIdFilter = products[0].Id
|
||||
builder = builder.Where("product_id = ?", productIdFilter)
|
||||
}
|
||||
|
||||
links, total, err := l.svcCtx.AgentLinkModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "id DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 批量查product_id->name,避免N+1
|
||||
productIdSet := make(map[int64]struct{})
|
||||
for _, link := range links {
|
||||
productIdSet[link.ProductId] = struct{}{}
|
||||
}
|
||||
productIdList := make([]int64, 0, len(productIdSet))
|
||||
for id := range productIdSet {
|
||||
productIdList = append(productIdList, id)
|
||||
}
|
||||
productNameMap := make(map[int64]string)
|
||||
if len(productIdList) > 0 {
|
||||
products, _ := l.svcCtx.ProductModel.FindAll(l.ctx, l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"id": productIdList}), "")
|
||||
for _, p := range products {
|
||||
productNameMap[p.Id] = p.ProductName
|
||||
}
|
||||
}
|
||||
|
||||
items := make([]types.AgentLinkListItem, 0, len(links))
|
||||
for _, link := range links {
|
||||
items = append(items, types.AgentLinkListItem{
|
||||
AgentId: link.AgentId,
|
||||
ProductName: productNameMap[link.ProductId],
|
||||
Price: link.Price,
|
||||
LinkIdentifier: link.LinkIdentifier,
|
||||
CreateTime: link.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
resp = &types.AdminGetAgentLinkListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetAgentListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetAgentListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentListLogic {
|
||||
return &AdminGetAgentListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetAgentListLogic) AdminGetAgentList(req *types.AdminGetAgentListReq) (resp *types.AdminGetAgentListResp, err error) {
|
||||
builder := l.svcCtx.AgentModel.SelectBuilder()
|
||||
if req.Mobile != nil && *req.Mobile != "" {
|
||||
builder = builder.Where("mobile = ?", *req.Mobile)
|
||||
}
|
||||
if req.Region != nil && *req.Region != "" {
|
||||
builder = builder.Where("region = ?", *req.Region)
|
||||
}
|
||||
|
||||
// 新增:如果传入ParentAgentId,则查找其所有1级下级代理
|
||||
if req.ParentAgentId != nil {
|
||||
closureBuilder := l.svcCtx.AgentClosureModel.SelectBuilder().Where("ancestor_id = ? AND depth = 1", *req.ParentAgentId)
|
||||
closures, cerr := l.svcCtx.AgentClosureModel.FindAll(l.ctx, closureBuilder, "")
|
||||
if cerr != nil {
|
||||
return nil, cerr
|
||||
}
|
||||
if len(closures) == 0 {
|
||||
resp = &types.AdminGetAgentListResp{Total: 0, Items: []types.AgentListItem{}}
|
||||
return resp, nil
|
||||
}
|
||||
ids := make([]int64, 0, len(closures))
|
||||
for _, c := range closures {
|
||||
ids = append(ids, c.DescendantId)
|
||||
}
|
||||
builder = builder.Where("id IN (?)", ids)
|
||||
}
|
||||
|
||||
agents, total, err := l.svcCtx.AgentModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "id DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]types.AgentListItem, 0, len(agents))
|
||||
|
||||
for _, agent := range agents {
|
||||
item := types.AgentListItem{
|
||||
Id: agent.Id,
|
||||
UserId: agent.UserId,
|
||||
LevelName: agent.LevelName,
|
||||
Region: agent.Region,
|
||||
CreateTime: agent.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
if req.ParentAgentId != nil {
|
||||
item.ParentAgentId = *req.ParentAgentId
|
||||
}
|
||||
agent.Mobile, err = crypto.DecryptMobile(agent.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息, 解密手机号失败: %v", err)
|
||||
}
|
||||
item.Mobile = agent.Mobile
|
||||
if agent.MembershipExpiryTime.Valid {
|
||||
item.MembershipExpiryTime = agent.MembershipExpiryTime.Time.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
// 查询钱包信息
|
||||
wallet, _ := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
if wallet != nil {
|
||||
item.Balance = wallet.Balance
|
||||
item.TotalEarnings = wallet.TotalEarnings
|
||||
item.FrozenBalance = wallet.FrozenBalance
|
||||
item.WithdrawnAmount = wallet.WithdrawnAmount
|
||||
}
|
||||
|
||||
// 查询实名认证信息
|
||||
realNameInfo, _ := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
if realNameInfo != nil {
|
||||
item.IsRealNameVerified = realNameInfo.Status == model.AgentRealNameStatusApproved
|
||||
item.RealName = realNameInfo.Name
|
||||
item.IdCard = realNameInfo.IdCard
|
||||
item.RealNameStatus = realNameInfo.Status
|
||||
} else {
|
||||
item.IsRealNameVerified = false
|
||||
item.RealName = ""
|
||||
item.IdCard = ""
|
||||
item.RealNameStatus = ""
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
resp = &types.AdminGetAgentListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetAgentMembershipConfigListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetAgentMembershipConfigListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentMembershipConfigListLogic {
|
||||
return &AdminGetAgentMembershipConfigListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetAgentMembershipConfigListLogic) AdminGetAgentMembershipConfigList(req *types.AdminGetAgentMembershipConfigListReq) (resp *types.AdminGetAgentMembershipConfigListResp, err error) {
|
||||
builder := l.svcCtx.AgentMembershipConfigModel.SelectBuilder()
|
||||
if req.LevelName != nil && *req.LevelName != "" {
|
||||
builder = builder.Where(squirrel.Eq{"level_name": *req.LevelName})
|
||||
}
|
||||
list, total, err := l.svcCtx.AgentMembershipConfigModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "id DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]types.AgentMembershipConfigListItem, 0, len(list))
|
||||
for _, v := range list {
|
||||
var item types.AgentMembershipConfigListItem
|
||||
if err := copier.Copy(&item, v); err != nil {
|
||||
l.Logger.Errorf("copy error: %v", err)
|
||||
continue
|
||||
}
|
||||
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
|
||||
items = append(items, item)
|
||||
}
|
||||
resp = &types.AdminGetAgentMembershipConfigListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetAgentMembershipRechargeOrderListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetAgentMembershipRechargeOrderListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentMembershipRechargeOrderListLogic {
|
||||
return &AdminGetAgentMembershipRechargeOrderListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetAgentMembershipRechargeOrderListLogic) AdminGetAgentMembershipRechargeOrderList(req *types.AdminGetAgentMembershipRechargeOrderListReq) (resp *types.AdminGetAgentMembershipRechargeOrderListResp, err error) {
|
||||
builder := l.svcCtx.AgentMembershipRechargeOrderModel.SelectBuilder()
|
||||
if req.UserId != nil {
|
||||
builder = builder.Where(squirrel.Eq{"user_id": *req.UserId})
|
||||
}
|
||||
if req.AgentId != nil {
|
||||
builder = builder.Where(squirrel.Eq{"agent_id": *req.AgentId})
|
||||
}
|
||||
if req.OrderNo != nil && *req.OrderNo != "" {
|
||||
builder = builder.Where(squirrel.Eq{"order_no": *req.OrderNo})
|
||||
}
|
||||
if req.PlatformOrderId != nil && *req.PlatformOrderId != "" {
|
||||
builder = builder.Where(squirrel.Eq{"platform_order_id": *req.PlatformOrderId})
|
||||
}
|
||||
if req.Status != nil && *req.Status != "" {
|
||||
builder = builder.Where(squirrel.Eq{"status": *req.Status})
|
||||
}
|
||||
if req.PaymentMethod != nil && *req.PaymentMethod != "" {
|
||||
builder = builder.Where(squirrel.Eq{"payment_method": *req.PaymentMethod})
|
||||
}
|
||||
list, total, err := l.svcCtx.AgentMembershipRechargeOrderModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]types.AgentMembershipRechargeOrderListItem, 0, len(list))
|
||||
for _, v := range list {
|
||||
item := types.AgentMembershipRechargeOrderListItem{}
|
||||
_ = copier.Copy(&item, v)
|
||||
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
|
||||
items = append(items, item)
|
||||
}
|
||||
resp = &types.AdminGetAgentMembershipRechargeOrderListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetAgentPlatformDeductionListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetAgentPlatformDeductionListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentPlatformDeductionListLogic {
|
||||
return &AdminGetAgentPlatformDeductionListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetAgentPlatformDeductionListLogic) AdminGetAgentPlatformDeductionList(req *types.AdminGetAgentPlatformDeductionListReq) (resp *types.AdminGetAgentPlatformDeductionListResp, err error) {
|
||||
builder := l.svcCtx.AgentPlatformDeductionModel.SelectBuilder()
|
||||
if req.AgentId != nil {
|
||||
builder = builder.Where(squirrel.Eq{"agent_id": *req.AgentId})
|
||||
}
|
||||
if req.Type != nil && *req.Type != "" {
|
||||
builder = builder.Where(squirrel.Eq{"type": *req.Type})
|
||||
}
|
||||
if req.Status != nil {
|
||||
builder = builder.Where(squirrel.Eq{"status": *req.Status})
|
||||
}
|
||||
|
||||
list, total, err := l.svcCtx.AgentPlatformDeductionModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]types.AgentPlatformDeductionListItem, 0, len(list))
|
||||
for _, v := range list {
|
||||
item := types.AgentPlatformDeductionListItem{}
|
||||
_ = copier.Copy(&item, v)
|
||||
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
|
||||
items = append(items, item)
|
||||
}
|
||||
resp = &types.AdminGetAgentPlatformDeductionListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetAgentProductionConfigListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetAgentProductionConfigListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentProductionConfigListLogic {
|
||||
return &AdminGetAgentProductionConfigListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetAgentProductionConfigListLogic) AdminGetAgentProductionConfigList(req *types.AdminGetAgentProductionConfigListReq) (resp *types.AdminGetAgentProductionConfigListResp, err error) {
|
||||
builder := l.svcCtx.AgentProductConfigModel.SelectBuilder()
|
||||
if req.ProductName != nil && *req.ProductName != "" {
|
||||
products, err := l.svcCtx.ProductModel.FindAll(l.ctx, l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"product_name": *req.ProductName}), "")
|
||||
if err != nil || len(products) == 0 {
|
||||
return &types.AdminGetAgentProductionConfigListResp{Total: 0, Items: []types.AgentProductionConfigItem{}}, nil
|
||||
}
|
||||
builder = builder.Where(squirrel.Eq{"product_id": products[0].Id})
|
||||
}
|
||||
if req.Id != nil {
|
||||
builder = builder.Where(squirrel.Eq{"id": *req.Id})
|
||||
}
|
||||
list, total, err := l.svcCtx.AgentProductConfigModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 查询所有涉及到的product_id对应的product_name
|
||||
productIdSet := make(map[int64]struct{})
|
||||
for _, v := range list {
|
||||
productIdSet[v.ProductId] = struct{}{}
|
||||
}
|
||||
productIdArr := make([]int64, 0, len(productIdSet))
|
||||
for id := range productIdSet {
|
||||
productIdArr = append(productIdArr, id)
|
||||
}
|
||||
productNameMap := make(map[int64]string)
|
||||
if len(productIdArr) > 0 {
|
||||
build := l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"id": productIdArr})
|
||||
products, _ := l.svcCtx.ProductModel.FindAll(l.ctx, build, "")
|
||||
for _, p := range products {
|
||||
productNameMap[p.Id] = p.ProductName
|
||||
}
|
||||
}
|
||||
items := make([]types.AgentProductionConfigItem, 0, len(list))
|
||||
for _, v := range list {
|
||||
item := types.AgentProductionConfigItem{}
|
||||
_ = copier.Copy(&item, v)
|
||||
item.ProductName = productNameMap[v.ProductId]
|
||||
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
|
||||
items = append(items, item)
|
||||
}
|
||||
resp = &types.AdminGetAgentProductionConfigListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetAgentRewardListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetAgentRewardListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentRewardListLogic {
|
||||
return &AdminGetAgentRewardListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetAgentRewardListLogic) AdminGetAgentRewardList(req *types.AdminGetAgentRewardListReq) (resp *types.AdminGetAgentRewardListResp, err error) {
|
||||
builder := l.svcCtx.AgentRewardsModel.SelectBuilder()
|
||||
if req.AgentId != nil {
|
||||
builder = builder.Where(squirrel.Eq{"agent_id": *req.AgentId})
|
||||
}
|
||||
if req.RelationAgentId != nil {
|
||||
builder = builder.Where(squirrel.Eq{"relation_agent_id": *req.RelationAgentId})
|
||||
}
|
||||
if req.Type != nil && *req.Type != "" {
|
||||
builder = builder.Where(squirrel.Eq{"type": *req.Type})
|
||||
}
|
||||
list, total, err := l.svcCtx.AgentRewardsModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]types.AgentRewardListItem, 0, len(list))
|
||||
for _, v := range list {
|
||||
item := types.AgentRewardListItem{}
|
||||
_ = copier.Copy(&item, v)
|
||||
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
|
||||
if v.RelationAgentId.Valid {
|
||||
item.RelationAgentId = v.RelationAgentId.Int64
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
resp = &types.AdminGetAgentRewardListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetAgentWithdrawalListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetAgentWithdrawalListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentWithdrawalListLogic {
|
||||
return &AdminGetAgentWithdrawalListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetAgentWithdrawalListLogic) AdminGetAgentWithdrawalList(req *types.AdminGetAgentWithdrawalListReq) (resp *types.AdminGetAgentWithdrawalListResp, err error) {
|
||||
builder := l.svcCtx.AgentWithdrawalModel.SelectBuilder()
|
||||
if req.AgentId != nil {
|
||||
builder = builder.Where(squirrel.Eq{"agent_id": *req.AgentId})
|
||||
}
|
||||
if req.Status != nil {
|
||||
builder = builder.Where(squirrel.Eq{"status": *req.Status})
|
||||
}
|
||||
if req.WithdrawNo != nil && *req.WithdrawNo != "" {
|
||||
builder = builder.Where(squirrel.Eq{"withdraw_no": *req.WithdrawNo})
|
||||
}
|
||||
list, total, err := l.svcCtx.AgentWithdrawalModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]types.AgentWithdrawalListItem, 0, len(list))
|
||||
for _, v := range list {
|
||||
item := types.AgentWithdrawalListItem{}
|
||||
_ = copier.Copy(&item, v)
|
||||
item.Remark = ""
|
||||
if v.Remark.Valid {
|
||||
item.Remark = v.Remark.String
|
||||
}
|
||||
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
|
||||
items = append(items, item)
|
||||
}
|
||||
resp = &types.AdminGetAgentWithdrawalListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminUpdateAgentMembershipConfigLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminUpdateAgentMembershipConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateAgentMembershipConfigLogic {
|
||||
return &AdminUpdateAgentMembershipConfigLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminUpdateAgentMembershipConfigLogic) AdminUpdateAgentMembershipConfig(req *types.AdminUpdateAgentMembershipConfigReq) (resp *types.AdminUpdateAgentMembershipConfigResp, err error) {
|
||||
cfg, err := l.svcCtx.AgentMembershipConfigModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.LevelName = req.LevelName
|
||||
cfg.Price = sql.NullFloat64{Float64: req.Price, Valid: true}
|
||||
cfg.ReportCommission = sql.NullFloat64{Float64: req.ReportCommission, Valid: true}
|
||||
if req.LowerActivityReward == nil {
|
||||
cfg.LowerActivityReward = sql.NullFloat64{Valid: false}
|
||||
} else {
|
||||
cfg.LowerActivityReward = sql.NullFloat64{Float64: *req.LowerActivityReward, Valid: true}
|
||||
}
|
||||
if req.NewActivityReward == nil {
|
||||
cfg.NewActivityReward = sql.NullFloat64{Valid: false}
|
||||
} else {
|
||||
cfg.NewActivityReward = sql.NullFloat64{Float64: *req.NewActivityReward, Valid: true}
|
||||
}
|
||||
if req.LowerStandardCount == nil {
|
||||
cfg.LowerStandardCount = sql.NullInt64{Valid: false}
|
||||
} else {
|
||||
cfg.LowerStandardCount = sql.NullInt64{Int64: *req.LowerStandardCount, Valid: true}
|
||||
}
|
||||
if req.NewLowerStandardCount == nil {
|
||||
cfg.NewLowerStandardCount = sql.NullInt64{Valid: false}
|
||||
} else {
|
||||
cfg.NewLowerStandardCount = sql.NullInt64{Int64: *req.NewLowerStandardCount, Valid: true}
|
||||
}
|
||||
if req.LowerWithdrawRewardRatio == nil {
|
||||
cfg.LowerWithdrawRewardRatio = sql.NullFloat64{Valid: false}
|
||||
} else {
|
||||
cfg.LowerWithdrawRewardRatio = sql.NullFloat64{Float64: *req.LowerWithdrawRewardRatio, Valid: true}
|
||||
}
|
||||
if req.LowerConvertVipReward == nil {
|
||||
cfg.LowerConvertVipReward = sql.NullFloat64{Valid: false}
|
||||
} else {
|
||||
cfg.LowerConvertVipReward = sql.NullFloat64{Float64: *req.LowerConvertVipReward, Valid: true}
|
||||
}
|
||||
if req.LowerConvertSvipReward == nil {
|
||||
cfg.LowerConvertSvipReward = sql.NullFloat64{Valid: false}
|
||||
} else {
|
||||
cfg.LowerConvertSvipReward = sql.NullFloat64{Float64: *req.LowerConvertSvipReward, Valid: true}
|
||||
}
|
||||
if req.ExemptionAmount == nil {
|
||||
cfg.ExemptionAmount = sql.NullFloat64{Valid: false}
|
||||
} else {
|
||||
cfg.ExemptionAmount = sql.NullFloat64{Float64: *req.ExemptionAmount, Valid: true}
|
||||
}
|
||||
if req.PriceIncreaseMax == nil {
|
||||
cfg.PriceIncreaseMax = sql.NullFloat64{Valid: false}
|
||||
} else {
|
||||
cfg.PriceIncreaseMax = sql.NullFloat64{Float64: *req.PriceIncreaseMax, Valid: true}
|
||||
}
|
||||
if req.PriceRatio == nil {
|
||||
cfg.PriceRatio = sql.NullFloat64{Valid: false}
|
||||
} else {
|
||||
cfg.PriceRatio = sql.NullFloat64{Float64: *req.PriceRatio, Valid: true}
|
||||
}
|
||||
if req.PriceIncreaseAmount == nil {
|
||||
cfg.PriceIncreaseAmount = sql.NullFloat64{Valid: false}
|
||||
} else {
|
||||
cfg.PriceIncreaseAmount = sql.NullFloat64{Float64: *req.PriceIncreaseAmount, Valid: true}
|
||||
}
|
||||
_, err = l.svcCtx.AgentMembershipConfigModel.Update(l.ctx, nil, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp = &types.AdminUpdateAgentMembershipConfigResp{Success: true}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminUpdateAgentProductionConfigLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminUpdateAgentProductionConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateAgentProductionConfigLogic {
|
||||
return &AdminUpdateAgentProductionConfigLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminUpdateAgentProductionConfigLogic) AdminUpdateAgentProductionConfig(req *types.AdminUpdateAgentProductionConfigReq) (resp *types.AdminUpdateAgentProductionConfigResp, err error) {
|
||||
cfg, err := l.svcCtx.AgentProductConfigModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.CostPrice = req.CostPrice
|
||||
cfg.PriceRangeMin = req.PriceRangeMin
|
||||
cfg.PriceRangeMax = req.PriceRangeMax
|
||||
cfg.PricingStandard = req.PricingStandard
|
||||
cfg.OverpricingRatio = req.OverpricingRatio
|
||||
_, err = l.svcCtx.AgentProductConfigModel.Update(l.ctx, nil, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp = &types.AdminUpdateAgentProductionConfigResp{Success: true}
|
||||
return
|
||||
}
|
||||
93
app/main/api/internal/logic/admin_auth/adminloginlogic.go
Normal file
93
app/main/api/internal/logic/admin_auth/adminloginlogic.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package admin_auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
jwtx "znc-server/common/jwt"
|
||||
"znc-server/common/xerr"
|
||||
"znc-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
|
||||
claims := jwtx.JwtClaims{
|
||||
UserId: user.Id,
|
||||
AgentId: 0,
|
||||
Platform: model.PlatformAdmin,
|
||||
UserType: model.UserTypeAdmin,
|
||||
IsAgent: model.AgentStatusNo,
|
||||
}
|
||||
token, err := jwtx.GenerateJwtToken(claims, 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
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package admin_feature
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminCreateFeatureLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminCreateFeatureLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminCreateFeatureLogic {
|
||||
return &AdminCreateFeatureLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminCreateFeatureLogic) AdminCreateFeature(req *types.AdminCreateFeatureReq) (resp *types.AdminCreateFeatureResp, err error) {
|
||||
// 1. 数据转换
|
||||
data := &model.Feature{
|
||||
ApiId: req.ApiId,
|
||||
Name: req.Name,
|
||||
}
|
||||
|
||||
// 2. 数据库操作
|
||||
result, err := l.svcCtx.FeatureModel.Insert(l.ctx, nil, data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"创建功能失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
|
||||
// 3. 返回结果
|
||||
id, _ := result.LastInsertId()
|
||||
return &types.AdminCreateFeatureResp{Id: id}, nil
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package admin_feature
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminDeleteFeatureLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminDeleteFeatureLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminDeleteFeatureLogic {
|
||||
return &AdminDeleteFeatureLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminDeleteFeatureLogic) AdminDeleteFeature(req *types.AdminDeleteFeatureReq) (resp *types.AdminDeleteFeatureResp, err error) {
|
||||
// 1. 查询记录是否存在
|
||||
record, err := l.svcCtx.FeatureModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查找功能失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
|
||||
// 2. 执行软删除
|
||||
err = l.svcCtx.FeatureModel.DeleteSoft(l.ctx, nil, record)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"删除功能失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
|
||||
// 3. 返回结果
|
||||
return &types.AdminDeleteFeatureResp{Success: true}, nil
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package admin_feature
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetFeatureDetailLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetFeatureDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetFeatureDetailLogic {
|
||||
return &AdminGetFeatureDetailLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetFeatureDetailLogic) AdminGetFeatureDetail(req *types.AdminGetFeatureDetailReq) (resp *types.AdminGetFeatureDetailResp, err error) {
|
||||
// 1. 查询记录
|
||||
record, err := l.svcCtx.FeatureModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查找功能失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
|
||||
// 2. 构建响应
|
||||
resp = &types.AdminGetFeatureDetailResp{
|
||||
Id: record.Id,
|
||||
ApiId: record.ApiId,
|
||||
Name: record.Name,
|
||||
CreateTime: record.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: record.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package admin_feature
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetFeatureListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetFeatureListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetFeatureListLogic {
|
||||
return &AdminGetFeatureListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetFeatureListLogic) AdminGetFeatureList(req *types.AdminGetFeatureListReq) (resp *types.AdminGetFeatureListResp, err error) {
|
||||
// 1. 构建查询条件
|
||||
builder := l.svcCtx.FeatureModel.SelectBuilder()
|
||||
|
||||
// 2. 添加查询条件
|
||||
if req.ApiId != nil && *req.ApiId != "" {
|
||||
builder = builder.Where("api_id LIKE ?", "%"+*req.ApiId+"%")
|
||||
}
|
||||
if req.Name != nil && *req.Name != "" {
|
||||
builder = builder.Where("name LIKE ?", "%"+*req.Name+"%")
|
||||
}
|
||||
|
||||
// 3. 执行分页查询
|
||||
list, total, err := l.svcCtx.FeatureModel.FindPageListByPageWithTotal(
|
||||
l.ctx, builder, req.Page, req.PageSize, "id DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查询功能列表失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
|
||||
// 4. 构建响应列表
|
||||
items := make([]types.FeatureListItem, 0, len(list))
|
||||
for _, item := range list {
|
||||
listItem := types.FeatureListItem{
|
||||
Id: item.Id,
|
||||
ApiId: item.ApiId,
|
||||
Name: item.Name,
|
||||
CreateTime: item.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: item.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
items = append(items, listItem)
|
||||
}
|
||||
|
||||
// 5. 返回结果
|
||||
return &types.AdminGetFeatureListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package admin_feature
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminUpdateFeatureLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminUpdateFeatureLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateFeatureLogic {
|
||||
return &AdminUpdateFeatureLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminUpdateFeatureLogic) AdminUpdateFeature(req *types.AdminUpdateFeatureReq) (resp *types.AdminUpdateFeatureResp, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return
|
||||
}
|
||||
97
app/main/api/internal/logic/admin_menu/createmenulogic.go
Normal file
97
app/main/api/internal/logic/admin_menu/createmenulogic.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package admin_menu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-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
|
||||
}
|
||||
30
app/main/api/internal/logic/admin_menu/deletemenulogic.go
Normal file
30
app/main/api/internal/logic/admin_menu/deletemenulogic.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package admin_menu
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-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
|
||||
}
|
||||
250
app/main/api/internal/logic/admin_menu/getmenualllogic.go
Normal file
250
app/main/api/internal/logic/admin_menu/getmenualllogic.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package admin_menu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-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
|
||||
}
|
||||
30
app/main/api/internal/logic/admin_menu/getmenudetaillogic.go
Normal file
30
app/main/api/internal/logic/admin_menu/getmenudetaillogic.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package admin_menu
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-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
|
||||
}
|
||||
109
app/main/api/internal/logic/admin_menu/getmenulistlogic.go
Normal file
109
app/main/api/internal/logic/admin_menu/getmenulistlogic.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package admin_menu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-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) (resp []types.MenuListItem, err 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
|
||||
}
|
||||
96
app/main/api/internal/logic/admin_menu/updatemenulogic.go
Normal file
96
app/main/api/internal/logic/admin_menu/updatemenulogic.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package admin_menu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-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
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package admin_notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminCreateNotificationLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminCreateNotificationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminCreateNotificationLogic {
|
||||
return &AdminCreateNotificationLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminCreateNotificationLogic) AdminCreateNotification(req *types.AdminCreateNotificationReq) (resp *types.AdminCreateNotificationResp, err error) {
|
||||
startDate, _ := time.Parse("2006-01-02", req.StartDate)
|
||||
endDate, _ := time.Parse("2006-01-02", req.EndDate)
|
||||
data := &model.GlobalNotifications{
|
||||
Title: req.Title,
|
||||
Content: req.Content,
|
||||
NotificationPage: req.NotificationPage,
|
||||
StartDate: sql.NullTime{Time: startDate, Valid: req.StartDate != ""},
|
||||
EndDate: sql.NullTime{Time: endDate, Valid: req.EndDate != ""},
|
||||
StartTime: req.StartTime,
|
||||
EndTime: req.EndTime,
|
||||
Status: req.Status,
|
||||
}
|
||||
result, err := l.svcCtx.GlobalNotificationsModel.Insert(l.ctx, nil, data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建通知失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
id, _ := result.LastInsertId()
|
||||
return &types.AdminCreateNotificationResp{Id: id}, nil
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package admin_notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminDeleteNotificationLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminDeleteNotificationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminDeleteNotificationLogic {
|
||||
return &AdminDeleteNotificationLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminDeleteNotificationLogic) AdminDeleteNotification(req *types.AdminDeleteNotificationReq) (resp *types.AdminDeleteNotificationResp, err error) {
|
||||
notification, err := l.svcCtx.GlobalNotificationsModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找通知失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
err = l.svcCtx.GlobalNotificationsModel.DeleteSoft(l.ctx, nil, notification)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "删除通知失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
return &types.AdminDeleteNotificationResp{Success: true}, nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package admin_notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetNotificationDetailLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetNotificationDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetNotificationDetailLogic {
|
||||
return &AdminGetNotificationDetailLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetNotificationDetailLogic) AdminGetNotificationDetail(req *types.AdminGetNotificationDetailReq) (resp *types.AdminGetNotificationDetailResp, err error) {
|
||||
notification, err := l.svcCtx.GlobalNotificationsModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找通知失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
resp = &types.AdminGetNotificationDetailResp{
|
||||
Id: notification.Id,
|
||||
Title: notification.Title,
|
||||
Content: notification.Content,
|
||||
NotificationPage: notification.NotificationPage,
|
||||
StartDate: "",
|
||||
StartTime: notification.StartTime,
|
||||
EndDate: "",
|
||||
EndTime: notification.EndTime,
|
||||
Status: notification.Status,
|
||||
CreateTime: notification.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: notification.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
if notification.StartDate.Valid {
|
||||
resp.StartDate = notification.StartDate.Time.Format("2006-01-02")
|
||||
}
|
||||
if notification.EndDate.Valid {
|
||||
resp.EndDate = notification.EndDate.Time.Format("2006-01-02")
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package admin_notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetNotificationListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetNotificationListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetNotificationListLogic {
|
||||
return &AdminGetNotificationListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetNotificationListLogic) AdminGetNotificationList(req *types.AdminGetNotificationListReq) (resp *types.AdminGetNotificationListResp, err error) {
|
||||
builder := l.svcCtx.GlobalNotificationsModel.SelectBuilder()
|
||||
if req.Title != nil {
|
||||
builder = builder.Where("title LIKE ?", "%"+*req.Title+"%")
|
||||
}
|
||||
if req.NotificationPage != nil {
|
||||
builder = builder.Where("notification_page = ?", *req.NotificationPage)
|
||||
}
|
||||
if req.Status != nil {
|
||||
builder = builder.Where("status = ?", *req.Status)
|
||||
}
|
||||
if req.StartDate != nil {
|
||||
if t, err := time.Parse("2006-01-02", *req.StartDate); err == nil {
|
||||
builder = builder.Where("start_date >= ?", t)
|
||||
}
|
||||
}
|
||||
if req.EndDate != nil {
|
||||
if t, err := time.Parse("2006-01-02", *req.EndDate); err == nil {
|
||||
builder = builder.Where("end_date <= ?", t)
|
||||
}
|
||||
}
|
||||
list, total, err := l.svcCtx.GlobalNotificationsModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "id DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询通知列表失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
items := make([]types.NotificationListItem, 0, len(list))
|
||||
for _, n := range list {
|
||||
item := types.NotificationListItem{
|
||||
Id: n.Id,
|
||||
Title: n.Title,
|
||||
NotificationPage: n.NotificationPage,
|
||||
Content: n.Content,
|
||||
StartDate: "",
|
||||
StartTime: n.StartTime,
|
||||
EndDate: "",
|
||||
EndTime: n.EndTime,
|
||||
Status: n.Status,
|
||||
CreateTime: n.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: n.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
if n.StartDate.Valid {
|
||||
item.StartDate = n.StartDate.Time.Format("2006-01-02")
|
||||
}
|
||||
if n.EndDate.Valid {
|
||||
item.EndDate = n.EndDate.Time.Format("2006-01-02")
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
resp = &types.AdminGetNotificationListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package admin_notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminUpdateNotificationLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminUpdateNotificationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateNotificationLogic {
|
||||
return &AdminUpdateNotificationLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminUpdateNotificationLogic) AdminUpdateNotification(req *types.AdminUpdateNotificationReq) (resp *types.AdminUpdateNotificationResp, err error) {
|
||||
notification, err := l.svcCtx.GlobalNotificationsModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找通知失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
if req.StartDate != nil {
|
||||
startDate, _ := time.Parse("2006-01-02", *req.StartDate)
|
||||
notification.StartDate = sql.NullTime{Time: startDate, Valid: true}
|
||||
}
|
||||
if req.EndDate != nil {
|
||||
endDate, _ := time.Parse("2006-01-02", *req.EndDate)
|
||||
notification.EndDate = sql.NullTime{Time: endDate, Valid: true}
|
||||
}
|
||||
if req.Title != nil {
|
||||
notification.Title = *req.Title
|
||||
}
|
||||
if req.Content != nil {
|
||||
notification.Content = *req.Content
|
||||
}
|
||||
if req.NotificationPage != nil {
|
||||
notification.NotificationPage = *req.NotificationPage
|
||||
}
|
||||
if req.StartTime != nil {
|
||||
notification.StartTime = *req.StartTime
|
||||
}
|
||||
if req.EndTime != nil {
|
||||
notification.EndTime = *req.EndTime
|
||||
}
|
||||
if req.Status != nil {
|
||||
notification.Status = *req.Status
|
||||
}
|
||||
_, err = l.svcCtx.GlobalNotificationsModel.Update(l.ctx, nil, notification)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新通知失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
return &types.AdminUpdateNotificationResp{Success: true}, nil
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package admin_order
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type AdminCreateOrderLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminCreateOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminCreateOrderLogic {
|
||||
return &AdminCreateOrderLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminCreateOrderLogic) AdminCreateOrder(req *types.AdminCreateOrderReq) (resp *types.AdminCreateOrderResp, err error) {
|
||||
// 生成订单号
|
||||
orderNo := fmt.Sprintf("%dADMIN", time.Now().UnixNano())
|
||||
|
||||
// 根据产品名称查询产品ID
|
||||
builder := l.svcCtx.ProductModel.SelectBuilder()
|
||||
builder = builder.Where("product_name = ? AND del_state = ?", req.ProductName, 0)
|
||||
products, err := l.svcCtx.ProductModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminCreateOrder, 查询产品失败 err: %v", err)
|
||||
}
|
||||
if len(products) == 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("产品不存在: %s", req.ProductName)), "AdminCreateOrder, 查询产品失败 err: %v", err)
|
||||
}
|
||||
product := products[0]
|
||||
|
||||
// 创建订单对象
|
||||
order := &model.Order{
|
||||
OrderNo: orderNo,
|
||||
PlatformOrderId: sql.NullString{String: req.PlatformOrderId, Valid: req.PlatformOrderId != ""},
|
||||
ProductId: product.Id,
|
||||
PaymentPlatform: req.PaymentPlatform,
|
||||
PaymentScene: req.PaymentScene,
|
||||
Amount: req.Amount,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
// 使用事务处理订单创建
|
||||
var orderId int64
|
||||
err = l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 插入订单
|
||||
result, err := l.svcCtx.OrderModel.Insert(ctx, session, order)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminCreateOrder, 创建订单失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 获取订单ID
|
||||
orderId, err = result.LastInsertId()
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminCreateOrder, 获取订单ID失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 如果是推广订单,创建推广订单记录
|
||||
if req.IsPromotion == 1 {
|
||||
promotionOrder := &model.AdminPromotionOrder{
|
||||
OrderId: orderId,
|
||||
Version: 1,
|
||||
CreateTime: time.Now(),
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
_, err = l.svcCtx.AdminPromotionOrderModel.Insert(ctx, session, promotionOrder)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminCreateOrder, 创建推广订单失败 err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.AdminCreateOrderResp{
|
||||
Id: orderId,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package admin_order
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type AdminDeleteOrderLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminDeleteOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminDeleteOrderLogic {
|
||||
return &AdminDeleteOrderLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminDeleteOrderLogic) AdminDeleteOrder(req *types.AdminDeleteOrderReq) (resp *types.AdminDeleteOrderResp, err error) {
|
||||
// 获取订单信息
|
||||
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminDeleteOrder, 查询订单失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 使用事务删除订单
|
||||
err = l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 软删除订单
|
||||
err := l.svcCtx.OrderModel.DeleteSoft(ctx, session, order)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminDeleteOrder, 删除订单失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 删除关联的推广订单记录
|
||||
promotionOrder, err := l.svcCtx.AdminPromotionOrderModel.FindOneByOrderId(ctx, order.Id)
|
||||
if err == nil && promotionOrder != nil {
|
||||
err = l.svcCtx.AdminPromotionOrderModel.DeleteSoft(ctx, session, promotionOrder)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminDeleteOrder, 删除推广订单失败 err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.AdminDeleteOrderResp{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package admin_order
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/globalkey"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetOrderDetailLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetOrderDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetOrderDetailLogic {
|
||||
return &AdminGetOrderDetailLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetOrderDetailLogic) AdminGetOrderDetail(req *types.AdminGetOrderDetailReq) (resp *types.AdminGetOrderDetailResp, err error) {
|
||||
// 获取订单信息
|
||||
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderDetail, 查询订单失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 获取产品信息
|
||||
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, order.ProductId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderDetail, 查询产品失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 判断是否为推广订单
|
||||
var isPromotion int64
|
||||
promotionOrder, err := l.svcCtx.AdminPromotionOrderModel.FindOneByOrderId(l.ctx, order.Id)
|
||||
if err == nil && promotionOrder != nil {
|
||||
isPromotion = 1
|
||||
}
|
||||
|
||||
// 获取查询状态
|
||||
var queryState string
|
||||
builder := l.svcCtx.QueryModel.SelectBuilder().Where("order_id = ?", order.Id).Columns("query_state")
|
||||
queries, err := l.svcCtx.QueryModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderDetail, 查询查询状态失败 err: %v", err)
|
||||
}
|
||||
|
||||
if len(queries) > 0 {
|
||||
queryState = queries[0].QueryState
|
||||
} else {
|
||||
// 查询清理日志
|
||||
cleanupBuilder := l.svcCtx.QueryCleanupDetailModel.SelectBuilder().
|
||||
Where("order_id = ?", order.Id).
|
||||
Where("del_state = ?", globalkey.DelStateNo).
|
||||
OrderBy("create_time DESC").
|
||||
Limit(1)
|
||||
cleanupDetails, err := l.svcCtx.QueryCleanupDetailModel.FindAll(l.ctx, cleanupBuilder, "")
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderDetail, 查询清理日志失败 err: %v", err)
|
||||
}
|
||||
|
||||
if len(cleanupDetails) > 0 {
|
||||
queryState = model.QueryStateCleaned
|
||||
} else {
|
||||
queryState = ""
|
||||
}
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
resp = &types.AdminGetOrderDetailResp{
|
||||
Id: order.Id,
|
||||
OrderNo: order.OrderNo,
|
||||
PlatformOrderId: order.PlatformOrderId.String,
|
||||
ProductName: product.ProductName,
|
||||
PaymentPlatform: order.PaymentPlatform,
|
||||
PaymentScene: order.PaymentScene,
|
||||
Amount: order.Amount,
|
||||
Status: order.Status,
|
||||
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: order.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
IsPromotion: isPromotion,
|
||||
QueryState: queryState,
|
||||
}
|
||||
|
||||
// 处理可选字段
|
||||
if order.PayTime.Valid {
|
||||
resp.PayTime = order.PayTime.Time.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if order.RefundTime.Valid {
|
||||
resp.RefundTime = order.RefundTime.Time.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package admin_order
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/globalkey"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
)
|
||||
|
||||
type AdminGetOrderListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetOrderListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetOrderListLogic {
|
||||
return &AdminGetOrderListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetOrderListLogic) AdminGetOrderList(req *types.AdminGetOrderListReq) (resp *types.AdminGetOrderListResp, err error) {
|
||||
// 构建查询条件
|
||||
builder := l.svcCtx.OrderModel.SelectBuilder()
|
||||
if req.OrderNo != "" {
|
||||
builder = builder.Where("order_no = ?", req.OrderNo)
|
||||
}
|
||||
if req.PlatformOrderId != "" {
|
||||
builder = builder.Where("platform_order_id = ?", req.PlatformOrderId)
|
||||
}
|
||||
if req.ProductName != "" {
|
||||
builder = builder.Where("product_id IN (SELECT id FROM product WHERE product_name LIKE ?)", "%"+req.ProductName+"%")
|
||||
}
|
||||
if req.PaymentPlatform != "" {
|
||||
builder = builder.Where("payment_platform = ?", req.PaymentPlatform)
|
||||
}
|
||||
if req.PaymentScene != "" {
|
||||
builder = builder.Where("payment_scene = ?", req.PaymentScene)
|
||||
}
|
||||
if req.Amount > 0 {
|
||||
builder = builder.Where("amount = ?", req.Amount)
|
||||
}
|
||||
if req.Status != "" {
|
||||
builder = builder.Where("status = ?", req.Status)
|
||||
}
|
||||
if req.IsPromotion != -1 {
|
||||
builder = builder.Where("id IN (SELECT order_id FROM admin_promotion_order WHERE del_state = 0)")
|
||||
}
|
||||
// 时间范围查询
|
||||
if req.CreateTimeStart != "" {
|
||||
builder = builder.Where("create_time >= ?", req.CreateTimeStart)
|
||||
}
|
||||
if req.CreateTimeEnd != "" {
|
||||
builder = builder.Where("create_time <= ?", req.CreateTimeEnd)
|
||||
}
|
||||
if req.PayTimeStart != "" {
|
||||
builder = builder.Where("pay_time >= ?", req.PayTimeStart)
|
||||
}
|
||||
if req.PayTimeEnd != "" {
|
||||
builder = builder.Where("pay_time <= ?", req.PayTimeEnd)
|
||||
}
|
||||
if req.RefundTimeStart != "" {
|
||||
builder = builder.Where("refund_time >= ?", req.RefundTimeStart)
|
||||
}
|
||||
if req.RefundTimeEnd != "" {
|
||||
builder = builder.Where("refund_time <= ?", req.RefundTimeEnd)
|
||||
}
|
||||
|
||||
// 并发获取总数和列表
|
||||
var total int64
|
||||
var orders []*model.Order
|
||||
err = mr.Finish(func() error {
|
||||
var err error
|
||||
total, err = l.svcCtx.OrderModel.FindCount(l.ctx, builder, "id")
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 查询订单总数失败 err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}, func() error {
|
||||
var err error
|
||||
orders, err = l.svcCtx.OrderModel.FindPageListByPage(l.ctx, builder, req.Page, req.PageSize, "id DESC")
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 查询订单列表失败 err: %v", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 并发获取产品信息和查询状态
|
||||
productMap := make(map[int64]string)
|
||||
queryStateMap := make(map[int64]string)
|
||||
var mu sync.Mutex
|
||||
|
||||
// 批量获取查询状态
|
||||
if len(orders) > 0 {
|
||||
orderIds := make([]int64, 0, len(orders))
|
||||
for _, order := range orders {
|
||||
orderIds = append(orderIds, order.Id)
|
||||
}
|
||||
|
||||
// 1. 先查询当前查询状态
|
||||
builder := l.svcCtx.QueryModel.SelectBuilder().
|
||||
Where(squirrel.Eq{"order_id": orderIds}).
|
||||
Columns("order_id", "query_state")
|
||||
queries, err := l.svcCtx.QueryModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 批量查询查询状态失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 2. 记录已找到查询状态的订单ID
|
||||
foundOrderIds := make(map[int64]bool)
|
||||
for _, query := range queries {
|
||||
queryStateMap[query.OrderId] = query.QueryState
|
||||
foundOrderIds[query.OrderId] = true
|
||||
}
|
||||
|
||||
// 3. 查找未找到查询状态的订单是否在清理日志中
|
||||
notFoundOrderIds := make([]int64, 0)
|
||||
for _, orderId := range orderIds {
|
||||
if !foundOrderIds[orderId] {
|
||||
notFoundOrderIds = append(notFoundOrderIds, orderId)
|
||||
}
|
||||
}
|
||||
|
||||
if len(notFoundOrderIds) > 0 {
|
||||
// 查询清理日志
|
||||
cleanupBuilder := l.svcCtx.QueryCleanupDetailModel.SelectBuilder().
|
||||
Where(squirrel.Eq{"order_id": notFoundOrderIds}).
|
||||
Where("del_state = ?", globalkey.DelStateNo).
|
||||
OrderBy("create_time DESC")
|
||||
cleanupDetails, err := l.svcCtx.QueryCleanupDetailModel.FindAll(l.ctx, cleanupBuilder, "")
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 查询清理日志失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 记录已清理的订单状态
|
||||
for _, detail := range cleanupDetails {
|
||||
if _, exists := queryStateMap[detail.OrderId]; !exists {
|
||||
queryStateMap[detail.OrderId] = model.QueryStateCleaned // 使用常量标记为已清除状态
|
||||
}
|
||||
}
|
||||
|
||||
// 对于既没有查询状态也没有清理记录的订单,不设置状态(保持为空字符串)
|
||||
for _, orderId := range notFoundOrderIds {
|
||||
if _, exists := queryStateMap[orderId]; !exists {
|
||||
queryStateMap[orderId] = "" // 未知状态保持为空字符串
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 并发获取产品信息
|
||||
err = mr.MapReduceVoid(func(source chan<- interface{}) {
|
||||
for _, order := range orders {
|
||||
source <- order
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[struct{}], cancel func(error)) {
|
||||
order := item.(*model.Order)
|
||||
|
||||
// 获取产品信息
|
||||
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, order.ProductId)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
cancel(errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 查询产品信息失败 err: %v", err))
|
||||
return
|
||||
}
|
||||
mu.Lock()
|
||||
if product != nil {
|
||||
productMap[product.Id] = product.ProductName
|
||||
} else {
|
||||
productMap[order.ProductId] = "" // 产品不存在时设置为空字符串
|
||||
}
|
||||
mu.Unlock()
|
||||
writer.Write(struct{}{})
|
||||
}, func(pipe <-chan struct{}, cancel func(error)) {
|
||||
for range pipe {
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
resp = &types.AdminGetOrderListResp{
|
||||
Total: total,
|
||||
Items: make([]types.OrderListItem, 0, len(orders)),
|
||||
}
|
||||
|
||||
for _, order := range orders {
|
||||
item := types.OrderListItem{
|
||||
Id: order.Id,
|
||||
OrderNo: order.OrderNo,
|
||||
PlatformOrderId: order.PlatformOrderId.String,
|
||||
ProductName: productMap[order.ProductId],
|
||||
PaymentPlatform: order.PaymentPlatform,
|
||||
PaymentScene: order.PaymentScene,
|
||||
Amount: order.Amount,
|
||||
Status: order.Status,
|
||||
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
QueryState: queryStateMap[order.Id],
|
||||
}
|
||||
if order.PayTime.Valid {
|
||||
item.PayTime = order.PayTime.Time.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if order.RefundTime.Valid {
|
||||
item.RefundTime = order.RefundTime.Time.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
// 判断是否为推广订单
|
||||
promotionOrder, err := l.svcCtx.AdminPromotionOrderModel.FindOneByOrderId(l.ctx, order.Id)
|
||||
if err == nil && promotionOrder != nil {
|
||||
item.IsPromotion = 1
|
||||
}
|
||||
resp.Items = append(resp.Items, item)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package admin_order
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type AdminRefundOrderLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminRefundOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminRefundOrderLogic {
|
||||
return &AdminRefundOrderLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminRefundOrderLogic) AdminRefundOrder(req *types.AdminRefundOrderReq) (resp *types.AdminRefundOrderResp, err error) {
|
||||
// 获取订单信息
|
||||
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminRefundOrder, 查询订单失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 检查订单状态
|
||||
if order.Status != "paid" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("订单状态不正确,无法退款"), "AdminRefundOrder, 订单状态不正确,无法退款 err: %v", err)
|
||||
}
|
||||
|
||||
// 检查退款金额
|
||||
if req.RefundAmount > order.Amount {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("退款金额不能大于订单金额"), "AdminRefundOrder, 退款金额不能大于订单金额 err: %v", err)
|
||||
}
|
||||
refundResp, err := l.svcCtx.AlipayService.AliRefund(l.ctx, order.OrderNo, req.RefundAmount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 退款失败 err: %v", err)
|
||||
}
|
||||
if refundResp.IsSuccess() {
|
||||
err = l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 创建退款记录
|
||||
refund := &model.OrderRefund{
|
||||
RefundNo: fmt.Sprintf("refund-%s", order.OrderNo),
|
||||
PlatformRefundId: sql.NullString{String: refundResp.TradeNo, Valid: true},
|
||||
OrderId: order.Id,
|
||||
UserId: order.UserId,
|
||||
ProductId: order.ProductId,
|
||||
RefundAmount: req.RefundAmount,
|
||||
RefundReason: sql.NullString{String: req.RefundReason, Valid: true},
|
||||
Status: model.OrderRefundStatusPending,
|
||||
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
}
|
||||
|
||||
if _, err := l.svcCtx.OrderRefundModel.Insert(ctx, session, refund); err != nil {
|
||||
return fmt.Errorf("创建退款记录失败: %v", err)
|
||||
}
|
||||
|
||||
// 更新订单状态
|
||||
order.Status = model.OrderStatusRefunded
|
||||
order.RefundTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if _, err := l.svcCtx.OrderModel.Update(ctx, session, order); err != nil {
|
||||
return fmt.Errorf("更新订单状态失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 退款失败 err: %v", err)
|
||||
}
|
||||
return &types.AdminRefundOrderResp{
|
||||
Status: model.OrderStatusRefunded,
|
||||
RefundNo: fmt.Sprintf("refund-%s", order.OrderNo),
|
||||
Amount: req.RefundAmount,
|
||||
}, nil
|
||||
} else {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("退款失败, : %v", refundResp.Msg)), "AdminRefundOrder, 退款失败 err: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
113
app/main/api/internal/logic/admin_order/adminupdateorderlogic.go
Normal file
113
app/main/api/internal/logic/admin_order/adminupdateorderlogic.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package admin_order
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type AdminUpdateOrderLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminUpdateOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateOrderLogic {
|
||||
return &AdminUpdateOrderLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminUpdateOrderLogic) AdminUpdateOrder(req *types.AdminUpdateOrderReq) (resp *types.AdminUpdateOrderResp, err error) {
|
||||
// 获取原订单信息
|
||||
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminUpdateOrder, 查询订单失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 更新订单字段
|
||||
if req.OrderNo != nil {
|
||||
order.OrderNo = *req.OrderNo
|
||||
}
|
||||
if req.PlatformOrderId != nil {
|
||||
order.PlatformOrderId = sql.NullString{String: *req.PlatformOrderId, Valid: true}
|
||||
}
|
||||
if req.PaymentPlatform != nil {
|
||||
order.PaymentPlatform = *req.PaymentPlatform
|
||||
}
|
||||
if req.PaymentScene != nil {
|
||||
order.PaymentScene = *req.PaymentScene
|
||||
}
|
||||
if req.Amount != nil {
|
||||
order.Amount = *req.Amount
|
||||
}
|
||||
if req.Status != nil {
|
||||
order.Status = *req.Status
|
||||
}
|
||||
if req.PayTime != nil {
|
||||
payTime, err := time.Parse("2006-01-02 15:04:05", *req.PayTime)
|
||||
if err == nil {
|
||||
order.PayTime = sql.NullTime{Time: payTime, Valid: true}
|
||||
}
|
||||
}
|
||||
if req.RefundTime != nil {
|
||||
refundTime, err := time.Parse("2006-01-02 15:04:05", *req.RefundTime)
|
||||
if err == nil {
|
||||
order.RefundTime = sql.NullTime{Time: refundTime, Valid: true}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用事务更新订单
|
||||
err = l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 更新订单
|
||||
_, err := l.svcCtx.OrderModel.Update(ctx, session, order)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminUpdateOrder, 更新订单失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 处理推广订单状态
|
||||
if req.IsPromotion != nil {
|
||||
promotionOrder, err := l.svcCtx.AdminPromotionOrderModel.FindOneByOrderId(ctx, order.Id)
|
||||
if err == nil && promotionOrder != nil {
|
||||
// 如果存在推广订单记录但不需要推广,则删除
|
||||
if *req.IsPromotion == 0 {
|
||||
err = l.svcCtx.AdminPromotionOrderModel.DeleteSoft(ctx, session, promotionOrder)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminUpdateOrder, 删除推广订单失败 err: %v", err)
|
||||
}
|
||||
}
|
||||
} else if *req.IsPromotion == 1 {
|
||||
// 如果需要推广但不存在记录,则创建
|
||||
newPromotionOrder := &model.AdminPromotionOrder{
|
||||
OrderId: order.Id,
|
||||
Version: 1,
|
||||
}
|
||||
_, err = l.svcCtx.AdminPromotionOrderModel.Insert(ctx, session, newPromotionOrder)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminUpdateOrder, 创建推广订单失败 err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.AdminUpdateOrderResp{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package admin_platform_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminCreatePlatformUserLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminCreatePlatformUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminCreatePlatformUserLogic {
|
||||
return &AdminCreatePlatformUserLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminCreatePlatformUserLogic) AdminCreatePlatformUser(req *types.AdminCreatePlatformUserReq) (resp *types.AdminCreatePlatformUserResp, err error) {
|
||||
// 校验手机号唯一性
|
||||
_, err = l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: req.Mobile, Valid: req.Mobile != ""})
|
||||
if err == nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机号已存在: %s", req.Mobile)
|
||||
}
|
||||
if err != model.ErrNotFound {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询手机号失败: %v", err)
|
||||
}
|
||||
|
||||
user := &model.User{
|
||||
Mobile: sql.NullString{String: req.Mobile, Valid: req.Mobile != ""},
|
||||
Password: sql.NullString{String: req.Password, Valid: req.Password != ""},
|
||||
Nickname: sql.NullString{String: req.Nickname, Valid: req.Nickname != ""},
|
||||
Info: req.Info,
|
||||
Inside: req.Inside,
|
||||
}
|
||||
result, err := l.svcCtx.UserModel.Insert(l.ctx, nil, user)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户失败: %v", err)
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取用户ID失败: %v", err)
|
||||
}
|
||||
resp = &types.AdminCreatePlatformUserResp{Id: id}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package admin_platform_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminDeletePlatformUserLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminDeletePlatformUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminDeletePlatformUserLogic {
|
||||
return &AdminDeletePlatformUserLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminDeletePlatformUserLogic) AdminDeletePlatformUser(req *types.AdminDeletePlatformUserReq) (resp *types.AdminDeletePlatformUserResp, err error) {
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户不存在: %d, err: %v", req.Id, err)
|
||||
}
|
||||
user.DelState = 1
|
||||
user.DeleteTime.Time = time.Now()
|
||||
user.DeleteTime.Valid = true
|
||||
err = l.svcCtx.UserModel.DeleteSoft(l.ctx, nil, user)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "软删除用户失败: %v", err)
|
||||
}
|
||||
resp = &types.AdminDeletePlatformUserResp{Success: true}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package admin_platform_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetPlatformUserDetailLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetPlatformUserDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetPlatformUserDetailLogic {
|
||||
return &AdminGetPlatformUserDetailLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetPlatformUserDetailLogic) AdminGetPlatformUserDetail(req *types.AdminGetPlatformUserDetailReq) (resp *types.AdminGetPlatformUserDetailResp, err error) {
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户不存在: %d, err: %v", req.Id, err)
|
||||
}
|
||||
key := l.svcCtx.Config.Encrypt.SecretKey
|
||||
DecryptMobile, err := crypto.DecryptMobile(user.Mobile.String, key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密手机号失败: %v", err)
|
||||
}
|
||||
// 查询平台类型(取第一个user_auth)
|
||||
resp = &types.AdminGetPlatformUserDetailResp{
|
||||
Id: user.Id,
|
||||
Mobile: DecryptMobile,
|
||||
Nickname: "",
|
||||
Info: user.Info,
|
||||
Inside: user.Inside,
|
||||
CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: user.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
if user.Nickname.Valid {
|
||||
resp.Nickname = user.Nickname.String
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package admin_platform_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetPlatformUserListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetPlatformUserListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetPlatformUserListLogic {
|
||||
return &AdminGetPlatformUserListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetPlatformUserListLogic) AdminGetPlatformUserList(req *types.AdminGetPlatformUserListReq) (resp *types.AdminGetPlatformUserListResp, err error) {
|
||||
builder := l.svcCtx.UserModel.SelectBuilder()
|
||||
if req.Mobile != "" {
|
||||
builder = builder.Where("mobile = ?", req.Mobile)
|
||||
}
|
||||
if req.Nickname != "" {
|
||||
builder = builder.Where("nickname = ?", req.Nickname)
|
||||
}
|
||||
if req.Inside != 0 {
|
||||
builder = builder.Where("inside = ?", req.Inside)
|
||||
}
|
||||
if req.CreateTimeStart != "" {
|
||||
builder = builder.Where("create_time >= ?", req.CreateTimeStart)
|
||||
}
|
||||
if req.CreateTimeEnd != "" {
|
||||
builder = builder.Where("create_time <= ?", req.CreateTimeEnd)
|
||||
}
|
||||
|
||||
orderBy := "id DESC"
|
||||
if req.OrderBy != "" && req.OrderType != "" {
|
||||
orderBy = fmt.Sprintf("%s %s", req.OrderBy, req.OrderType)
|
||||
}
|
||||
users, total, err := l.svcCtx.UserModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, orderBy)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户分页失败: %v", err)
|
||||
}
|
||||
var items []types.PlatformUserListItem
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
|
||||
for _, user := range users {
|
||||
mobile := user.Mobile
|
||||
if mobile.Valid {
|
||||
encryptedMobile, err := crypto.DecryptMobile(mobile.String, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 解密手机号失败: %+v", err)
|
||||
}
|
||||
mobile = sql.NullString{String: encryptedMobile, Valid: true}
|
||||
}
|
||||
itemData := types.PlatformUserListItem{
|
||||
Id: user.Id,
|
||||
Mobile: mobile.String,
|
||||
Nickname: "",
|
||||
Info: user.Info,
|
||||
Inside: user.Inside,
|
||||
CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: user.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
if user.Nickname.Valid {
|
||||
itemData.Nickname = user.Nickname.String
|
||||
}
|
||||
items = append(items, itemData)
|
||||
}
|
||||
resp = &types.AdminGetPlatformUserListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package admin_platform_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminUpdatePlatformUserLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminUpdatePlatformUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdatePlatformUserLogic {
|
||||
return &AdminUpdatePlatformUserLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminUpdatePlatformUserLogic) AdminUpdatePlatformUser(req *types.AdminUpdatePlatformUserReq) (resp *types.AdminUpdatePlatformUserResp, err error) {
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户不存在: %d, err: %v", req.Id, err)
|
||||
}
|
||||
if req.Mobile != nil {
|
||||
key := l.svcCtx.Config.Encrypt.SecretKey
|
||||
EncryptMobile, err := crypto.EncryptMobile(*req.Mobile, key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
|
||||
}
|
||||
user.Mobile = sql.NullString{String: EncryptMobile, Valid: true}
|
||||
}
|
||||
if req.Nickname != nil {
|
||||
user.Nickname = sql.NullString{String: *req.Nickname, Valid: *req.Nickname != ""}
|
||||
}
|
||||
if req.Info != nil {
|
||||
user.Info = *req.Info
|
||||
}
|
||||
if req.Inside != nil {
|
||||
if *req.Inside != 1 && *req.Inside != 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "内部用户状态错误: %d", *req.Inside)
|
||||
}
|
||||
user.Inside = *req.Inside
|
||||
}
|
||||
if req.Password != nil {
|
||||
user.Password = sql.NullString{String: *req.Password, Valid: *req.Password != ""}
|
||||
}
|
||||
_, err = l.svcCtx.UserModel.Update(l.ctx, nil, user)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新用户失败: %v", err)
|
||||
}
|
||||
resp = &types.AdminUpdatePlatformUserResp{Success: true}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package admin_product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminCreateProductLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminCreateProductLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminCreateProductLogic {
|
||||
return &AdminCreateProductLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminCreateProductLogic) AdminCreateProduct(req *types.AdminCreateProductReq) (resp *types.AdminCreateProductResp, err error) {
|
||||
// 1. 数据转换
|
||||
data := &model.Product{
|
||||
ProductName: req.ProductName,
|
||||
ProductEn: req.ProductEn,
|
||||
Description: req.Description,
|
||||
Notes: sql.NullString{String: req.Notes, Valid: req.Notes != ""},
|
||||
CostPrice: req.CostPrice,
|
||||
SellPrice: req.SellPrice,
|
||||
}
|
||||
|
||||
// 2. 数据库操作
|
||||
result, err := l.svcCtx.ProductModel.Insert(l.ctx, nil, data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"创建产品失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
|
||||
// 3. 返回结果
|
||||
id, _ := result.LastInsertId()
|
||||
return &types.AdminCreateProductResp{Id: id}, nil
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package admin_product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminDeleteProductLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminDeleteProductLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminDeleteProductLogic {
|
||||
return &AdminDeleteProductLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminDeleteProductLogic) AdminDeleteProduct(req *types.AdminDeleteProductReq) (resp *types.AdminDeleteProductResp, err error) {
|
||||
// 1. 查询记录是否存在
|
||||
record, err := l.svcCtx.ProductModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查找产品失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
|
||||
// 2. 执行软删除
|
||||
err = l.svcCtx.ProductModel.DeleteSoft(l.ctx, nil, record)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"删除产品失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
|
||||
// 3. 返回结果
|
||||
return &types.AdminDeleteProductResp{Success: true}, nil
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package admin_product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetProductDetailLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetProductDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetProductDetailLogic {
|
||||
return &AdminGetProductDetailLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetProductDetailLogic) AdminGetProductDetail(req *types.AdminGetProductDetailReq) (resp *types.AdminGetProductDetailResp, err error) {
|
||||
// 1. 查询记录
|
||||
record, err := l.svcCtx.ProductModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查找产品失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
|
||||
// 2. 构建响应
|
||||
resp = &types.AdminGetProductDetailResp{
|
||||
Id: record.Id,
|
||||
ProductName: record.ProductName,
|
||||
ProductEn: record.ProductEn,
|
||||
Description: record.Description,
|
||||
Notes: record.Notes.String,
|
||||
CostPrice: record.CostPrice,
|
||||
SellPrice: record.SellPrice,
|
||||
CreateTime: record.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: record.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package admin_product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
)
|
||||
|
||||
type AdminGetProductFeatureListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetProductFeatureListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetProductFeatureListLogic {
|
||||
return &AdminGetProductFeatureListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetProductFeatureListLogic) AdminGetProductFeatureList(req *types.AdminGetProductFeatureListReq) (resp *[]types.AdminGetProductFeatureListResp, err error) {
|
||||
// 1. 构建查询条件
|
||||
builder := l.svcCtx.ProductFeatureModel.SelectBuilder().
|
||||
Where("product_id = ?", req.ProductId)
|
||||
|
||||
// 2. 执行查询
|
||||
list, err := l.svcCtx.ProductFeatureModel.FindAll(l.ctx, builder, "sort ASC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查询产品功能列表失败, err: %v, product_id: %d", err, req.ProductId)
|
||||
}
|
||||
|
||||
// 3. 获取所有功能ID
|
||||
featureIds := make([]int64, 0, len(list))
|
||||
for _, item := range list {
|
||||
featureIds = append(featureIds, item.FeatureId)
|
||||
}
|
||||
|
||||
// 4. 并发查询功能详情
|
||||
type featureResult struct {
|
||||
feature *model.Feature
|
||||
err error
|
||||
}
|
||||
|
||||
results := make([]featureResult, len(featureIds))
|
||||
err = mr.MapReduceVoid(func(source chan<- interface{}) {
|
||||
for i, id := range featureIds {
|
||||
source <- struct {
|
||||
index int
|
||||
id int64
|
||||
}{i, id}
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[featureResult], cancel func(error)) {
|
||||
data := item.(struct {
|
||||
index int
|
||||
id int64
|
||||
})
|
||||
feature, err := l.svcCtx.FeatureModel.FindOne(l.ctx, data.id)
|
||||
writer.Write(featureResult{
|
||||
feature: feature,
|
||||
err: err,
|
||||
})
|
||||
}, func(pipe <-chan featureResult, cancel func(error)) {
|
||||
for result := range pipe {
|
||||
if result.err != nil {
|
||||
l.Logger.Errorf("查询功能详情失败, feature_id: %d, err: %v", result.feature.Id, result.err)
|
||||
continue
|
||||
}
|
||||
results = append(results, result)
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR),
|
||||
"并发查询功能详情失败, err: %v", err)
|
||||
}
|
||||
|
||||
// 5. 构建功能ID到详情的映射
|
||||
featureMap := make(map[int64]*model.Feature)
|
||||
for _, result := range results {
|
||||
if result.feature != nil {
|
||||
featureMap[result.feature.Id] = result.feature
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 构建响应列表
|
||||
items := make([]types.AdminGetProductFeatureListResp, 0, len(list))
|
||||
for _, item := range list {
|
||||
feature, exists := featureMap[item.FeatureId]
|
||||
if !exists {
|
||||
continue // 跳过不存在的功能
|
||||
}
|
||||
|
||||
listItem := types.AdminGetProductFeatureListResp{
|
||||
Id: item.Id,
|
||||
ProductId: item.ProductId,
|
||||
FeatureId: item.FeatureId,
|
||||
ApiId: feature.ApiId,
|
||||
Name: feature.Name,
|
||||
Sort: item.Sort,
|
||||
Enable: item.Enable,
|
||||
IsImportant: item.IsImportant,
|
||||
CreateTime: item.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: item.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
items = append(items, listItem)
|
||||
}
|
||||
|
||||
// 7. 返回结果
|
||||
return &items, nil
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package admin_product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetProductListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetProductListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetProductListLogic {
|
||||
return &AdminGetProductListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetProductListLogic) AdminGetProductList(req *types.AdminGetProductListReq) (resp *types.AdminGetProductListResp, err error) {
|
||||
// 1. 构建查询条件
|
||||
builder := l.svcCtx.ProductModel.SelectBuilder()
|
||||
|
||||
// 2. 添加查询条件
|
||||
if req.ProductName != nil && *req.ProductName != "" {
|
||||
builder = builder.Where("product_name LIKE ?", "%"+*req.ProductName+"%")
|
||||
}
|
||||
if req.ProductEn != nil && *req.ProductEn != "" {
|
||||
builder = builder.Where("product_en LIKE ?", "%"+*req.ProductEn+"%")
|
||||
}
|
||||
|
||||
// 3. 执行分页查询
|
||||
list, total, err := l.svcCtx.ProductModel.FindPageListByPageWithTotal(
|
||||
l.ctx, builder, req.Page, req.PageSize, "id DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查询产品列表失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
|
||||
// 4. 构建响应列表
|
||||
items := make([]types.ProductListItem, 0, len(list))
|
||||
for _, item := range list {
|
||||
listItem := types.ProductListItem{
|
||||
Id: item.Id,
|
||||
ProductName: item.ProductName,
|
||||
ProductEn: item.ProductEn,
|
||||
Description: item.Description,
|
||||
Notes: item.Notes.String,
|
||||
CostPrice: item.CostPrice,
|
||||
SellPrice: item.SellPrice,
|
||||
CreateTime: item.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: item.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
items = append(items, listItem)
|
||||
}
|
||||
|
||||
// 5. 返回结果
|
||||
return &types.AdminGetProductListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package admin_product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type AdminUpdateProductFeaturesLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminUpdateProductFeaturesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateProductFeaturesLogic {
|
||||
return &AdminUpdateProductFeaturesLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminUpdateProductFeaturesLogic) AdminUpdateProductFeatures(req *types.AdminUpdateProductFeaturesReq) (resp *types.AdminUpdateProductFeaturesResp, err error) {
|
||||
// 1. 查询现有关联
|
||||
builder := l.svcCtx.ProductFeatureModel.SelectBuilder().
|
||||
Where("product_id = ?", req.ProductId)
|
||||
existingList, err := l.svcCtx.ProductFeatureModel.FindAll(l.ctx, builder, "id ASC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查询现有产品功能关联失败, err: %v, product_id: %d", err, req.ProductId)
|
||||
}
|
||||
|
||||
// 2. 构建现有关联的映射
|
||||
existingMap := make(map[int64]*model.ProductFeature)
|
||||
for _, item := range existingList {
|
||||
existingMap[item.FeatureId] = item
|
||||
}
|
||||
|
||||
// 3. 构建新关联的映射
|
||||
newMap := make(map[int64]*types.ProductFeatureItem)
|
||||
for _, item := range req.Features {
|
||||
newMap[item.FeatureId] = &item
|
||||
}
|
||||
|
||||
// 4. 在事务中执行更新操作
|
||||
err = l.svcCtx.ProductFeatureModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 4.1 处理需要删除的关联
|
||||
var mu sync.Mutex
|
||||
var deleteIds []int64
|
||||
err = mr.MapReduceVoid(func(source chan<- interface{}) {
|
||||
for featureId, existing := range existingMap {
|
||||
if _, exists := newMap[featureId]; !exists {
|
||||
source <- existing.Id
|
||||
}
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[struct{}], cancel func(error)) {
|
||||
id := item.(int64)
|
||||
mu.Lock()
|
||||
deleteIds = append(deleteIds, id)
|
||||
mu.Unlock()
|
||||
}, func(pipe <-chan struct{}, cancel func(error)) {
|
||||
// 等待所有ID收集完成
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "收集待删除ID失败")
|
||||
}
|
||||
|
||||
// 批量删除
|
||||
if len(deleteIds) > 0 {
|
||||
for _, id := range deleteIds {
|
||||
err = l.svcCtx.ProductFeatureModel.Delete(ctx, session, id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "删除产品功能关联失败, product_id: %d, id: %d",
|
||||
req.ProductId, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4.2 并发处理需要新增或更新的关联
|
||||
var updateErr error
|
||||
err = mr.MapReduceVoid(func(source chan<- interface{}) {
|
||||
for featureId, newItem := range newMap {
|
||||
source <- struct {
|
||||
featureId int64
|
||||
newItem *types.ProductFeatureItem
|
||||
existing *model.ProductFeature
|
||||
}{
|
||||
featureId: featureId,
|
||||
newItem: newItem,
|
||||
existing: existingMap[featureId],
|
||||
}
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[struct{}], cancel func(error)) {
|
||||
data := item.(struct {
|
||||
featureId int64
|
||||
newItem *types.ProductFeatureItem
|
||||
existing *model.ProductFeature
|
||||
})
|
||||
|
||||
if data.existing != nil {
|
||||
// 更新现有关联
|
||||
data.existing.Sort = data.newItem.Sort
|
||||
data.existing.Enable = data.newItem.Enable
|
||||
data.existing.IsImportant = data.newItem.IsImportant
|
||||
_, err = l.svcCtx.ProductFeatureModel.Update(ctx, session, data.existing)
|
||||
if err != nil {
|
||||
updateErr = errors.Wrapf(err, "更新产品功能关联失败, product_id: %d, feature_id: %d",
|
||||
req.ProductId, data.featureId)
|
||||
cancel(updateErr)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// 新增关联
|
||||
newFeature := &model.ProductFeature{
|
||||
ProductId: req.ProductId,
|
||||
FeatureId: data.featureId,
|
||||
Sort: data.newItem.Sort,
|
||||
Enable: data.newItem.Enable,
|
||||
IsImportant: data.newItem.IsImportant,
|
||||
}
|
||||
_, err = l.svcCtx.ProductFeatureModel.Insert(ctx, session, newFeature)
|
||||
if err != nil {
|
||||
updateErr = errors.Wrapf(err, "新增产品功能关联失败, product_id: %d, feature_id: %d",
|
||||
req.ProductId, data.featureId)
|
||||
cancel(updateErr)
|
||||
return
|
||||
}
|
||||
}
|
||||
}, func(pipe <-chan struct{}, cancel func(error)) {
|
||||
// 等待所有更新完成
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "并发更新产品功能关联失败")
|
||||
}
|
||||
if updateErr != nil {
|
||||
return updateErr
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"更新产品功能关联失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
|
||||
// 5. 返回结果
|
||||
return &types.AdminUpdateProductFeaturesResp{Success: true}, nil
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package admin_product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminUpdateProductLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminUpdateProductLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateProductLogic {
|
||||
return &AdminUpdateProductLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminUpdateProductLogic) AdminUpdateProduct(req *types.AdminUpdateProductReq) (resp *types.AdminUpdateProductResp, err error) {
|
||||
// 1. 查询记录是否存在
|
||||
record, err := l.svcCtx.ProductModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查找产品失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
|
||||
// 2. 更新字段
|
||||
if req.ProductName != nil {
|
||||
record.ProductName = *req.ProductName
|
||||
}
|
||||
if req.ProductEn != nil {
|
||||
record.ProductEn = *req.ProductEn
|
||||
}
|
||||
if req.Description != nil {
|
||||
record.Description = *req.Description
|
||||
}
|
||||
if req.Notes != nil {
|
||||
record.Notes = sql.NullString{String: *req.Notes, Valid: *req.Notes != ""}
|
||||
}
|
||||
if req.CostPrice != nil {
|
||||
record.CostPrice = *req.CostPrice
|
||||
}
|
||||
if req.SellPrice != nil {
|
||||
record.SellPrice = *req.SellPrice
|
||||
}
|
||||
|
||||
// 3. 执行更新操作
|
||||
_, err = l.svcCtx.ProductModel.Update(l.ctx, nil, record)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"更新产品失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
|
||||
// 4. 返回结果
|
||||
return &types.AdminUpdateProductResp{Success: true}, nil
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-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
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-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
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-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
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-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
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-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
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-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)
|
||||
}
|
||||
|
||||
// 获取今日统计数据
|
||||
now := time.Now()
|
||||
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
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), "无效的推广链接路径")
|
||||
}
|
||||
|
||||
// 使用 statsService 更新点击统计
|
||||
err = l.svcCtx.AdminPromotionLinkStatsService.UpdateLinkStats(l.ctx, link.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新点击统计失败: %+v", err)
|
||||
}
|
||||
|
||||
return &types.RecordLinkClickResp{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-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
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package admin_query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/globalkey"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetQueryCleanupConfigListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetQueryCleanupConfigListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetQueryCleanupConfigListLogic {
|
||||
return &AdminGetQueryCleanupConfigListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetQueryCleanupConfigListLogic) AdminGetQueryCleanupConfigList(req *types.AdminGetQueryCleanupConfigListReq) (resp *types.AdminGetQueryCleanupConfigListResp, err error) {
|
||||
// 构建查询条件
|
||||
builder := l.svcCtx.QueryCleanupConfigModel.SelectBuilder().
|
||||
Where("del_state = ?", globalkey.DelStateNo)
|
||||
|
||||
if req.Status > 0 {
|
||||
builder = builder.Where("status = ?", req.Status)
|
||||
}
|
||||
|
||||
// 查询配置列表
|
||||
configs, err := l.svcCtx.QueryCleanupConfigModel.FindAll(l.ctx, builder, "id ASC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询清理配置列表失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
resp = &types.AdminGetQueryCleanupConfigListResp{
|
||||
Items: make([]types.QueryCleanupConfigItem, 0, len(configs)),
|
||||
}
|
||||
|
||||
for _, config := range configs {
|
||||
item := types.QueryCleanupConfigItem{
|
||||
Id: config.Id,
|
||||
ConfigKey: config.ConfigKey,
|
||||
ConfigValue: config.ConfigValue,
|
||||
ConfigDesc: config.ConfigDesc,
|
||||
Status: config.Status,
|
||||
CreateTime: config.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: config.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
resp.Items = append(resp.Items, item)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package admin_query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/globalkey"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
)
|
||||
|
||||
type AdminGetQueryCleanupDetailListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetQueryCleanupDetailListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetQueryCleanupDetailListLogic {
|
||||
return &AdminGetQueryCleanupDetailListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetQueryCleanupDetailListLogic) AdminGetQueryCleanupDetailList(req *types.AdminGetQueryCleanupDetailListReq) (resp *types.AdminGetQueryCleanupDetailListResp, err error) {
|
||||
// 1. 验证清理日志是否存在
|
||||
_, err = l.svcCtx.QueryCleanupLogModel.FindOne(l.ctx, req.LogId)
|
||||
if err != nil {
|
||||
if err == model.ErrNotFound {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "清理日志不存在, log_id: %d", req.LogId)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询清理日志失败, log_id: %d, err: %v", req.LogId, err)
|
||||
}
|
||||
|
||||
// 2. 构建查询条件
|
||||
builder := l.svcCtx.QueryCleanupDetailModel.SelectBuilder().
|
||||
Where("cleanup_log_id = ?", req.LogId).
|
||||
Where("del_state = ?", globalkey.DelStateNo)
|
||||
|
||||
// 3. 并发获取总数和列表
|
||||
var total int64
|
||||
var details []*model.QueryCleanupDetail
|
||||
err = mr.Finish(func() error {
|
||||
var err error
|
||||
total, err = l.svcCtx.QueryCleanupDetailModel.FindCount(l.ctx, builder, "id")
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询清理详情总数失败 err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}, func() error {
|
||||
var err error
|
||||
details, err = l.svcCtx.QueryCleanupDetailModel.FindPageListByPage(l.ctx, builder, req.Page, req.PageSize, "id DESC")
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询清理详情列表失败 err: %v", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 4. 获取所有产品ID
|
||||
productIds := make([]int64, 0, len(details))
|
||||
for _, detail := range details {
|
||||
productIds = append(productIds, detail.ProductId)
|
||||
}
|
||||
|
||||
// 5. 并发获取产品信息
|
||||
productMap := make(map[int64]string)
|
||||
var mu sync.Mutex
|
||||
err = mr.MapReduceVoid(func(source chan<- interface{}) {
|
||||
for _, productId := range productIds {
|
||||
source <- productId
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[struct{}], cancel func(error)) {
|
||||
productId := item.(int64)
|
||||
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, productId)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
cancel(errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询产品信息失败, product_id: %d, err: %v", productId, err))
|
||||
return
|
||||
}
|
||||
mu.Lock()
|
||||
if product != nil {
|
||||
productMap[productId] = product.ProductName
|
||||
} else {
|
||||
productMap[productId] = "" // 产品不存在时设置为空字符串
|
||||
}
|
||||
mu.Unlock()
|
||||
writer.Write(struct{}{})
|
||||
}, func(pipe <-chan struct{}, cancel func(error)) {
|
||||
for range pipe {
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 6. 构建响应
|
||||
resp = &types.AdminGetQueryCleanupDetailListResp{
|
||||
Total: total,
|
||||
Items: make([]types.QueryCleanupDetailItem, 0, len(details)),
|
||||
}
|
||||
|
||||
for _, detail := range details {
|
||||
item := types.QueryCleanupDetailItem{
|
||||
Id: detail.Id,
|
||||
CleanupLogId: detail.CleanupLogId,
|
||||
QueryId: detail.QueryId,
|
||||
OrderId: detail.OrderId,
|
||||
UserId: detail.UserId,
|
||||
ProductName: productMap[detail.ProductId],
|
||||
QueryState: detail.QueryState,
|
||||
CreateTimeOld: detail.CreateTimeOld.Format("2006-01-02 15:04:05"),
|
||||
CreateTime: detail.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
resp.Items = append(resp.Items, item)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package admin_query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/globalkey"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
)
|
||||
|
||||
type AdminGetQueryCleanupLogListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetQueryCleanupLogListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetQueryCleanupLogListLogic {
|
||||
return &AdminGetQueryCleanupLogListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetQueryCleanupLogListLogic) AdminGetQueryCleanupLogList(req *types.AdminGetQueryCleanupLogListReq) (resp *types.AdminGetQueryCleanupLogListResp, err error) {
|
||||
// 构建查询条件
|
||||
builder := l.svcCtx.QueryCleanupLogModel.SelectBuilder().
|
||||
Where("del_state = ?", globalkey.DelStateNo)
|
||||
|
||||
if req.Status > 0 {
|
||||
builder = builder.Where("status = ?", req.Status)
|
||||
}
|
||||
if req.StartTime != "" {
|
||||
builder = builder.Where("cleanup_time >= ?", req.StartTime)
|
||||
}
|
||||
if req.EndTime != "" {
|
||||
builder = builder.Where("cleanup_time <= ?", req.EndTime)
|
||||
}
|
||||
|
||||
// 并发获取总数和列表
|
||||
var total int64
|
||||
var logs []*model.QueryCleanupLog
|
||||
err = mr.Finish(func() error {
|
||||
var err error
|
||||
total, err = l.svcCtx.QueryCleanupLogModel.FindCount(l.ctx, builder, "id")
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询清理日志总数失败 err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}, func() error {
|
||||
var err error
|
||||
logs, err = l.svcCtx.QueryCleanupLogModel.FindPageListByPage(l.ctx, builder, req.Page, req.PageSize, "id DESC")
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询清理日志列表失败 err: %v", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
resp = &types.AdminGetQueryCleanupLogListResp{
|
||||
Total: total,
|
||||
Items: make([]types.QueryCleanupLogItem, 0, len(logs)),
|
||||
}
|
||||
|
||||
for _, log := range logs {
|
||||
item := types.QueryCleanupLogItem{
|
||||
Id: log.Id,
|
||||
CleanupTime: log.CleanupTime.Format("2006-01-02 15:04:05"),
|
||||
CleanupBefore: log.CleanupBefore.Format("2006-01-02 15:04:05"),
|
||||
Status: log.Status,
|
||||
AffectedRows: log.AffectedRows,
|
||||
ErrorMsg: log.ErrorMsg.String,
|
||||
Remark: log.Remark.String,
|
||||
CreateTime: log.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
resp.Items = append(resp.Items, item)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package admin_query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
"znc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AdminGetQueryDetailByOrderIdLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminGetQueryDetailByOrderIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetQueryDetailByOrderIdLogic {
|
||||
return &AdminGetQueryDetailByOrderIdLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminGetQueryDetailByOrderIdLogic) AdminGetQueryDetailByOrderId(req *types.AdminGetQueryDetailByOrderIdReq) (resp *types.AdminGetQueryDetailByOrderIdResp, err error) {
|
||||
|
||||
// 获取报告信息
|
||||
queryModel, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, req.OrderId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询, 查找报告错误: %+v", err)
|
||||
}
|
||||
|
||||
var query types.AdminGetQueryDetailByOrderIdResp
|
||||
query.CreateTime = queryModel.CreateTime.Format("2006-01-02 15:04:05")
|
||||
query.UpdateTime = queryModel.UpdateTime.Format("2006-01-02 15:04:05")
|
||||
|
||||
// 解密查询数据
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
key, decodeErr := hex.DecodeString(secretKey)
|
||||
if decodeErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 获取AES解密解药失败, %+v", err)
|
||||
}
|
||||
processParamsErr := ProcessQueryParams(queryModel.QueryParams, &query.QueryParams, key)
|
||||
if processParamsErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告参数处理失败: %v", processParamsErr)
|
||||
}
|
||||
processErr := ProcessQueryData(queryModel.QueryData, &query.QueryData, key)
|
||||
if processErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结果处理失败: %v", processErr)
|
||||
}
|
||||
updateFeatureAndProductFeatureErr := l.UpdateFeatureAndProductFeature(queryModel.ProductId, &query.QueryData)
|
||||
if updateFeatureAndProductFeatureErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结果处理失败: %v", updateFeatureAndProductFeatureErr)
|
||||
}
|
||||
// 复制报告数据
|
||||
err = copier.Copy(&query, queryModel)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结构体复制失败, %v", err)
|
||||
}
|
||||
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, queryModel.ProductId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 获取商品信息失败, %v", err)
|
||||
}
|
||||
query.ProductName = product.ProductName
|
||||
return &types.AdminGetQueryDetailByOrderIdResp{
|
||||
Id: query.Id,
|
||||
OrderId: query.OrderId,
|
||||
UserId: query.UserId,
|
||||
ProductName: query.ProductName,
|
||||
QueryParams: query.QueryParams,
|
||||
QueryData: query.QueryData,
|
||||
CreateTime: query.CreateTime,
|
||||
UpdateTime: query.UpdateTime,
|
||||
QueryState: query.QueryState,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ProcessQueryData 解密和反序列化 QueryData
|
||||
func ProcessQueryData(queryData sql.NullString, target *[]types.AdminQueryItem, key []byte) error {
|
||||
queryDataStr := lzUtils.NullStringToString(queryData)
|
||||
if queryDataStr == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解密数据
|
||||
decryptedData, decryptErr := crypto.AesDecrypt(queryDataStr, key)
|
||||
if decryptErr != nil {
|
||||
return decryptErr
|
||||
}
|
||||
|
||||
// 解析 JSON 数组
|
||||
var decryptedArray []map[string]interface{}
|
||||
unmarshalErr := json.Unmarshal(decryptedData, &decryptedArray)
|
||||
if unmarshalErr != nil {
|
||||
return unmarshalErr
|
||||
}
|
||||
|
||||
// 确保 target 具有正确的长度
|
||||
if len(*target) == 0 {
|
||||
*target = make([]types.AdminQueryItem, len(decryptedArray))
|
||||
}
|
||||
|
||||
// 填充解密后的数据到 target
|
||||
for i := 0; i < len(decryptedArray); i++ {
|
||||
// 直接填充解密数据到 Data 字段
|
||||
(*target)[i].Data = decryptedArray[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessQueryParams解密和反序列化 QueryParams
|
||||
func ProcessQueryParams(QueryParams string, target *map[string]interface{}, key []byte) error {
|
||||
// 解密 QueryParams
|
||||
decryptedData, decryptErr := crypto.AesDecrypt(QueryParams, key)
|
||||
if decryptErr != nil {
|
||||
return decryptErr
|
||||
}
|
||||
|
||||
// 反序列化解密后的数据
|
||||
unmarshalErr := json.Unmarshal(decryptedData, target)
|
||||
if unmarshalErr != nil {
|
||||
return unmarshalErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *AdminGetQueryDetailByOrderIdLogic) UpdateFeatureAndProductFeature(productID int64, target *[]types.AdminQueryItem) error {
|
||||
// 遍历 target 数组,使用倒序遍历,以便删除元素时不影响索引
|
||||
for i := len(*target) - 1; i >= 0; i-- {
|
||||
queryItem := &(*target)[i]
|
||||
|
||||
// 确保 Data 为 map 类型
|
||||
data, ok := queryItem.Data.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("queryItem.Data 必须是 map[string]interface{} 类型")
|
||||
}
|
||||
|
||||
// 从 Data 中获取 apiID
|
||||
apiID, ok := data["apiID"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("queryItem.Data 中的 apiID 必须是字符串类型")
|
||||
}
|
||||
|
||||
// 查询 Feature
|
||||
feature, err := l.svcCtx.FeatureModel.FindOneByApiId(l.ctx, apiID)
|
||||
if err != nil {
|
||||
// 如果 Feature 查不到,也要删除当前 QueryItem
|
||||
*target = append((*target)[:i], (*target)[i+1:]...)
|
||||
continue
|
||||
}
|
||||
|
||||
// 查询 ProductFeatureModel
|
||||
builder := l.svcCtx.ProductFeatureModel.SelectBuilder().Where("product_id = ?", productID)
|
||||
productFeatures, err := l.svcCtx.ProductFeatureModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询 ProductFeatureModel 错误: %v", err)
|
||||
}
|
||||
|
||||
// 遍历 productFeatures,找到与 feature.ID 关联且 enable == 1 的项
|
||||
var featureData map[string]interface{}
|
||||
sort := 0
|
||||
for _, pf := range productFeatures {
|
||||
if pf.FeatureId == feature.Id { // 确保和 Feature 关联
|
||||
sort = int(pf.Sort)
|
||||
break // 找到第一个符合条件的就退出循环
|
||||
}
|
||||
}
|
||||
featureData = map[string]interface{}{
|
||||
"featureName": feature.Name,
|
||||
"sort": sort,
|
||||
}
|
||||
|
||||
// 更新 queryItem 的 Feature 字段(不是数组)
|
||||
queryItem.Feature = featureData
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package admin_query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type AdminUpdateQueryCleanupConfigLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminUpdateQueryCleanupConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateQueryCleanupConfigLogic {
|
||||
return &AdminUpdateQueryCleanupConfigLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminUpdateQueryCleanupConfigLogic) AdminUpdateQueryCleanupConfig(req *types.AdminUpdateQueryCleanupConfigReq) (resp *types.AdminUpdateQueryCleanupConfigResp, err error) {
|
||||
// 使用事务处理更新操作
|
||||
err = l.svcCtx.QueryCleanupConfigModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 1. 查询配置是否存在
|
||||
config, err := l.svcCtx.QueryCleanupConfigModel.FindOne(ctx, req.Id)
|
||||
if err != nil {
|
||||
if err == model.ErrNotFound {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "配置不存在, id: %d", req.Id)
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询配置失败, id: %d, err: %v", req.Id, err)
|
||||
}
|
||||
|
||||
// 2. 更新配置
|
||||
config.ConfigValue = req.ConfigValue
|
||||
config.Status = req.Status
|
||||
config.UpdateTime = time.Now()
|
||||
|
||||
_, err = l.svcCtx.QueryCleanupConfigModel.Update(ctx, session, config)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新配置失败, id: %d, err: %v", req.Id, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.AdminUpdateQueryCleanupConfigResp{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
83
app/main/api/internal/logic/admin_role/createrolelogic.go
Normal file
83
app/main/api/internal/logic/admin_role/createrolelogic.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package admin_role
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-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
|
||||
}
|
||||
84
app/main/api/internal/logic/admin_role/deleterolelogic.go
Normal file
84
app/main/api/internal/logic/admin_role/deleterolelogic.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package admin_role
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-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
|
||||
}
|
||||
91
app/main/api/internal/logic/admin_role/getroledetaillogic.go
Normal file
91
app/main/api/internal/logic/admin_role/getroledetaillogic.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package admin_role
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-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
|
||||
}
|
||||
148
app/main/api/internal/logic/admin_role/getrolelistlogic.go
Normal file
148
app/main/api/internal/logic/admin_role/getrolelistlogic.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package admin_role
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-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
|
||||
}
|
||||
148
app/main/api/internal/logic/admin_role/updaterolelogic.go
Normal file
148
app/main/api/internal/logic/admin_role/updaterolelogic.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package admin_role
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-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
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package admin_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/xerr"
|
||||
"znc-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
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package admin_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-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
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package admin_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-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
|
||||
}
|
||||
149
app/main/api/internal/logic/admin_user/admingetuserlistlogic.go
Normal file
149
app/main/api/internal/logic/admin_user/admingetuserlistlogic.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package admin_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-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.AdminUserListItem, 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.AdminUserListItem
|
||||
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.AdminUserListItem{
|
||||
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
|
||||
}
|
||||
141
app/main/api/internal/logic/admin_user/adminupdateuserlogic.go
Normal file
141
app/main/api/internal/logic/admin_user/adminupdateuserlogic.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package admin_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-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
|
||||
}
|
||||
67
app/main/api/internal/logic/admin_user/adminuserinfologic.go
Normal file
67
app/main/api/internal/logic/admin_user/adminuserinfologic.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package admin_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-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
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ActivateAgentMembershipLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewActivateAgentMembershipLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ActivateAgentMembershipLogic {
|
||||
return &ActivateAgentMembershipLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
func (l *ActivateAgentMembershipLogic) ActivateAgentMembership(req *types.AgentActivateMembershipReq) (resp *types.AgentActivateMembershipResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %v", err)
|
||||
}
|
||||
// 查询用户代理信息
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err)
|
||||
}
|
||||
// 定义等级顺序映射
|
||||
levelOrder := map[string]int{
|
||||
"": 1,
|
||||
model.AgentLeveNameNormal: 1,
|
||||
model.AgentLeveNameVIP: 2,
|
||||
model.AgentLeveNameSVIP: 3,
|
||||
}
|
||||
|
||||
// 验证请求等级合法性
|
||||
if _, valid := levelOrder[req.Type]; !valid {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "无效的代理等级: %s", req.Type)
|
||||
}
|
||||
|
||||
// 如果存在代理记录,进行等级验证
|
||||
if agentModel != nil {
|
||||
currentLevel, exists := levelOrder[agentModel.LevelName]
|
||||
if !exists {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR),
|
||||
"非法的当前代理等级: %s", agentModel.LevelName)
|
||||
}
|
||||
|
||||
requestedLevel := levelOrder[req.Type]
|
||||
if requestedLevel < currentLevel {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR),
|
||||
"禁止降级操作(当前等级:%s,请求等级:%s)", agentModel.LevelName, req.Type)
|
||||
}
|
||||
// 同等级视为续费,允许操作
|
||||
}
|
||||
outTradeNo := "A_" + l.svcCtx.AlipayService.GenerateOutTradeNo()
|
||||
redisKey := fmt.Sprintf(types.AgentVipCacheKey, userID, outTradeNo)
|
||||
agentVipCache := types.AgentVipCache{Type: req.Type}
|
||||
jsonData, err := json.Marshal(agentVipCache)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "序列化代理VIP缓存失败: %v", err)
|
||||
}
|
||||
cacheErr := l.svcCtx.Redis.SetexCtx(l.ctx, redisKey, string(jsonData), int(2*time.Hour))
|
||||
if cacheErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "设置缓存失败: %v", cacheErr)
|
||||
}
|
||||
return &types.AgentActivateMembershipResp{
|
||||
Id: outTradeNo,
|
||||
}, nil
|
||||
}
|
||||
99
app/main/api/internal/logic/agent/agentrealnamelogic.go
Normal file
99
app/main/api/internal/logic/agent/agentrealnamelogic.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/service"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
)
|
||||
|
||||
type AgentRealNameLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAgentRealNameLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AgentRealNameLogic {
|
||||
return &AgentRealNameLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AgentRealNameLogic) AgentRealName(req *types.AgentRealNameReq) (resp *types.AgentRealNameResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败, %v", err)
|
||||
}
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理实名, 加密手机号失败: %v", err)
|
||||
}
|
||||
// 检查手机号是否在一分钟内已发送过验证码
|
||||
redisKey := fmt.Sprintf("%s:%s", "realName", encryptedMobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "代理实名, 验证码过期: %s", encryptedMobile)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理实名, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "代理实名, 验证码不正确: %s", encryptedMobile)
|
||||
}
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理实名信息失败, %v", err)
|
||||
}
|
||||
|
||||
if agentRealName != nil && agentRealName.Status == model.AgentRealNameStatusApproved {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("代理实名信息已审核通过"), "代理实名信息已审核通过")
|
||||
}
|
||||
// 三要素验证
|
||||
threeVerification := service.ThreeFactorVerificationRequest{
|
||||
Name: req.Name,
|
||||
IDCard: req.IDCard,
|
||||
Mobile: req.Mobile,
|
||||
}
|
||||
verification, err := l.svcCtx.VerificationService.ThreeFactorVerification(threeVerification)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "三要素验证失败: %v", err)
|
||||
}
|
||||
if !verification.Passed {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.SERVER_COMMON_ERROR, verification.Err.Error()), "三要素验证不通过: %v", err)
|
||||
}
|
||||
agentRealName = &model.AgentRealName{
|
||||
AgentId: agent.Id,
|
||||
Status: model.AgentRealNameStatusApproved,
|
||||
Name: req.Name,
|
||||
IdCard: req.IDCard,
|
||||
ApproveTime: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
}
|
||||
_, err = l.svcCtx.AgentRealNameModel.Insert(l.ctx, nil, agentRealName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "添加代理实名信息失败, %v", err)
|
||||
}
|
||||
|
||||
return &types.AgentRealNameResp{
|
||||
Status: agentRealName.Status,
|
||||
}, nil
|
||||
}
|
||||
450
app/main/api/internal/logic/agent/agentwithdrawallogic.go
Normal file
450
app/main/api/internal/logic/agent/agentwithdrawallogic.go
Normal file
@@ -0,0 +1,450 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smartwalle/alipay/v3"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
// 状态常量
|
||||
const (
|
||||
StatusProcessing = 1 // 处理中
|
||||
StatusSuccess = 2 // 成功
|
||||
StatusFailed = 3 // 失败
|
||||
)
|
||||
|
||||
// 前端响应状态
|
||||
const (
|
||||
WithdrawStatusProcessing = 1
|
||||
WithdrawStatusSuccess = 2
|
||||
WithdrawStatusFailed = 3
|
||||
)
|
||||
|
||||
type AgentWithdrawalLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAgentWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AgentWithdrawalLogic {
|
||||
return &AgentWithdrawalLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types.WithdrawalResp, error) {
|
||||
var (
|
||||
outBizNo string
|
||||
withdrawRes = &types.WithdrawalResp{}
|
||||
)
|
||||
var finalWithdrawAmount float64 // 实际到账金额
|
||||
// 使用事务处理核心操作
|
||||
err := l.svcCtx.AgentModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %v", err)
|
||||
}
|
||||
|
||||
// 查询代理信息
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err)
|
||||
}
|
||||
agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agentModel.Id)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您未进行实名认证, 无法提现"), "您未进行实名认证")
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理实名信息失败: %v", err)
|
||||
}
|
||||
if agentRealName.Status != model.AgentRealNameStatusApproved {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您的实名认证未通过, 无法提现"), "您的实名认证未通过")
|
||||
}
|
||||
if agentRealName.Name != req.PayeeName {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您的实名认证信息不匹配, 无法提现"), "您的实名认证信息不匹配")
|
||||
}
|
||||
// 查询钱包
|
||||
agentWallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agentModel.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理钱包失败: %v", err)
|
||||
}
|
||||
|
||||
// 校验可提现金额
|
||||
if req.Amount > agentWallet.Balance {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您可提现的余额不足"), "获取用户ID失败")
|
||||
}
|
||||
|
||||
// 生成交易号
|
||||
outBizNo = "W_" + l.svcCtx.AlipayService.GenerateOutTradeNo()
|
||||
|
||||
// 冻结资金(事务内操作)
|
||||
if err = l.freezeFunds(session, agentWallet, req.Amount); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "资金冻结失败: %v", err)
|
||||
}
|
||||
yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
|
||||
// 计算税务额度
|
||||
taxExemption, err := l.svcCtx.AgentWithdrawalTaxExemptionModel.FindOneByAgentIdYearMonth(l.ctx, agentModel.Id, yearMonth)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
taxExemption, err = l.createMonthlyExemption(session, agentModel.Id, yearMonth)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建代理税务额度失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理税务额度失败: %v", err)
|
||||
}
|
||||
}
|
||||
var taxAmount float64 // 应缴税费
|
||||
var taxDeductionPart float64 // 应税金额
|
||||
var TaxStatus int64 // 扣税状态
|
||||
var exemptionAmount float64 // 免税金额
|
||||
taxRate := l.svcCtx.Config.TaxConfig.TaxRate
|
||||
|
||||
if taxExemption.RemainingExemptionAmount < req.Amount {
|
||||
// 超过免税额度需要扣税
|
||||
exemptionAmount = taxExemption.RemainingExemptionAmount // 免税金额 = 剩余免税额度
|
||||
TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税
|
||||
taxDeductionPart = req.Amount - taxExemption.RemainingExemptionAmount // 应税金额 = 提现金额 - 剩余免税额度
|
||||
taxAmount = taxDeductionPart * taxRate // 应缴税费 = 应税金额 * 税率
|
||||
finalWithdrawAmount = req.Amount - taxAmount // 实际到账金额 = 提现金额 - 应缴税费
|
||||
|
||||
taxExemption.UsedExemptionAmount += exemptionAmount // 已使用免税额度 = 已使用免税额度 + 免税金额
|
||||
taxExemption.RemainingExemptionAmount -= exemptionAmount // 剩余免税额度 = 剩余免税额度 - 免税金额
|
||||
err = l.svcCtx.AgentWithdrawalTaxExemptionModel.UpdateWithVersion(l.ctx, session, taxExemption)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新代理税务额度失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 未超过免税额度,免税
|
||||
exemptionAmount = req.Amount // 免税金额 = 提现金额
|
||||
TaxStatus = model.TaxStatusExempt // 扣税状态 = 免税
|
||||
taxDeductionPart = 0 // 应税金额 = 0
|
||||
finalWithdrawAmount = req.Amount // 实际到账金额 = 提现金额
|
||||
taxAmount = 0 // 应缴税费 = 0
|
||||
taxExemption.UsedExemptionAmount += exemptionAmount // 已使用免税额度 = 已使用免税额度 + 免税金额
|
||||
taxExemption.RemainingExemptionAmount -= exemptionAmount // 剩余免税额度 = 剩余免税额度 - 免税金额
|
||||
err = l.svcCtx.AgentWithdrawalTaxExemptionModel.UpdateWithVersion(l.ctx, session, taxExemption)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新代理税务额度失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建提现记录(初始状态为处理中)
|
||||
withdrawalID, err := l.createWithdrawalRecord(session, agentModel.Id, req.PayeeAccount, req.Amount, finalWithdrawAmount, taxAmount, outBizNo)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建提现记录失败: %v", err)
|
||||
}
|
||||
// 扣税记录
|
||||
taxModel := &model.AgentWithdrawalTax{
|
||||
AgentId: agentModel.Id,
|
||||
YearMonth: yearMonth,
|
||||
WithdrawalId: withdrawalID,
|
||||
WithdrawalAmount: req.Amount,
|
||||
ExemptionAmount: exemptionAmount,
|
||||
TaxableAmount: taxDeductionPart,
|
||||
TaxRate: taxRate,
|
||||
TaxAmount: taxAmount,
|
||||
ActualAmount: finalWithdrawAmount,
|
||||
TaxStatus: TaxStatus,
|
||||
Remark: sql.NullString{String: "提现成功自动扣税", Valid: true},
|
||||
ExemptionRecordId: taxExemption.Id,
|
||||
}
|
||||
_, err = l.svcCtx.AgentWithdrawalTaxModel.Insert(ctx, session, taxModel)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建扣税记录失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 同步调用支付宝转账
|
||||
transferResp, err := l.svcCtx.AlipayService.AliTransfer(l.ctx, req.PayeeAccount, req.PayeeName, finalWithdrawAmount, "代理提现", outBizNo)
|
||||
if err != nil {
|
||||
l.Logger.Errorf("【支付宝转账失败】outBizNo:%s error:%v", outBizNo, err)
|
||||
l.handleTransferError(outBizNo, err, "支付宝接口调用失败")
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "支付宝接口调用失败: %v", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case transferResp.Status == "SUCCESS":
|
||||
// 立即处理成功状态
|
||||
l.handleTransferSuccess(outBizNo, transferResp)
|
||||
withdrawRes.Status = WithdrawStatusSuccess
|
||||
case transferResp.Status == "FAIL" || transferResp.SubCode != "":
|
||||
// 处理明确失败
|
||||
errorMsg := l.mapAlipayError(transferResp.SubCode)
|
||||
l.handleTransferFailure(outBizNo, transferResp)
|
||||
withdrawRes.Status = WithdrawStatusFailed
|
||||
withdrawRes.FailMsg = errorMsg
|
||||
case transferResp.Status == "DEALING":
|
||||
// 处理中状态,启动异步轮询
|
||||
go l.startAsyncPolling(outBizNo)
|
||||
withdrawRes.Status = WithdrawStatusProcessing
|
||||
default:
|
||||
// 未知状态按失败处理
|
||||
l.handleTransferError(outBizNo, fmt.Errorf("未知状态:%s", transferResp.Status), "支付宝返回未知状态")
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "支付宝接口调用失败: %v", err)
|
||||
}
|
||||
return withdrawRes, nil
|
||||
}
|
||||
|
||||
// 错误类型映射
|
||||
func (l *AgentWithdrawalLogic) mapAlipayError(code string) string {
|
||||
errorMapping := map[string]string{
|
||||
// 账户存在性错误
|
||||
"PAYEE_ACCOUNT_NOT_EXSIT": "收款账户不存在,请检查账号是否正确",
|
||||
"PAYEE_NOT_EXIST": "收款账户不存在或姓名有误,请核实信息",
|
||||
"PAYEE_ACC_OCUPIED": "收款账号存在多个账户,无法确认唯一性",
|
||||
"PAYEE_MID_CANNOT_SAME": "收款方和中间方不能是同一个人,请修改收款方或者中间方信息",
|
||||
|
||||
// 实名认证问题
|
||||
"PAYEE_CERTIFY_LEVEL_LIMIT": "收款方未完成实名认证",
|
||||
"PAYEE_NOT_RELNAME_CERTIFY": "收款方未完成实名认证",
|
||||
"PAYEE_CERT_INFO_ERROR": "收款方证件信息不匹配",
|
||||
|
||||
// 账户状态异常
|
||||
"PAYEE_ACCOUNT_STATUS_ERROR": "收款账户状态异常,请更换账号",
|
||||
"PAYEE_USERINFO_STATUS_ERROR": "收款账户状态异常,无法收款",
|
||||
"PERMIT_LIMIT_PAYEE": "收款账户异常,请更换账号",
|
||||
"BLOCK_USER_FORBBIDEN_RECIEVE": "账户冻结无法收款",
|
||||
"PAYEE_TRUSTEESHIP_ACC_OVER_LIMIT": "收款方托管子户累计收款金额超限",
|
||||
|
||||
// 账户信息错误
|
||||
"PAYEE_USERINFO_ERROR": "收款方姓名或信息不匹配",
|
||||
"PAYEE_CARD_INFO_ERROR": "收款支付宝账号及户名不一致",
|
||||
"PAYEE_IDENTITY_NOT_MATCH": "收款方身份信息不匹配",
|
||||
"PAYEE_USER_IS_INST": "收款方为金融机构,不能使用提现功能,请更换收款账号",
|
||||
"PAYEE_USER_TYPE_ERROR": "该支付宝账号类型不支持提现,请更换收款账号",
|
||||
|
||||
// 权限与限制
|
||||
"PAYEE_RECEIVE_COUNT_EXCEED_LIMIT": "收款次数超限,请明日再试",
|
||||
"PAYEE_OUT_PERMLIMIT_CHECK_FAILURE": "收款方权限校验不通过",
|
||||
"PERMIT_NON_BANK_LIMIT_PAYEE": "收款方未完善身份信息,无法收款",
|
||||
}
|
||||
if msg, ok := errorMapping[code]; ok {
|
||||
return msg
|
||||
}
|
||||
return "系统错误,请联系客服"
|
||||
}
|
||||
|
||||
// 创建提现记录(事务内操作)
|
||||
func (l *AgentWithdrawalLogic) createWithdrawalRecord(session sqlx.Session, agentID int64, payeeAccount string, amount float64, finalWithdrawAmount float64, taxAmount float64, outBizNo string) (int64, error) {
|
||||
record := &model.AgentWithdrawal{
|
||||
AgentId: agentID,
|
||||
WithdrawNo: outBizNo,
|
||||
PayeeAccount: payeeAccount,
|
||||
Amount: amount,
|
||||
ActualAmount: finalWithdrawAmount,
|
||||
TaxAmount: taxAmount,
|
||||
Status: StatusProcessing,
|
||||
}
|
||||
|
||||
result, err := l.svcCtx.AgentWithdrawalModel.Insert(l.ctx, session, record)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.LastInsertId()
|
||||
}
|
||||
|
||||
// 冻结资金(事务内操作)
|
||||
func (l *AgentWithdrawalLogic) freezeFunds(session sqlx.Session, wallet *model.AgentWallet, amount float64) error {
|
||||
wallet.Balance -= amount
|
||||
wallet.FrozenBalance += amount
|
||||
err := l.svcCtx.AgentWalletModel.UpdateWithVersion(l.ctx, session, wallet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理异步轮询
|
||||
func (l *AgentWithdrawalLogic) startAsyncPolling(outBizNo string) {
|
||||
go func() {
|
||||
detachedCtx := context.WithoutCancel(l.ctx)
|
||||
retryConfig := &backoff.ExponentialBackOff{
|
||||
InitialInterval: 10 * time.Second,
|
||||
RandomizationFactor: 0.5, // 增加随机因子防止惊群
|
||||
Multiplier: 2,
|
||||
MaxInterval: 30 * time.Second,
|
||||
MaxElapsedTime: 5 * time.Minute, // 缩短总超时
|
||||
Clock: backoff.SystemClock,
|
||||
}
|
||||
retryConfig.Reset()
|
||||
operation := func() error {
|
||||
statusRsp, err := l.svcCtx.AlipayService.QueryTransferStatus(detachedCtx, outBizNo)
|
||||
if err != nil {
|
||||
return err // 触发重试
|
||||
}
|
||||
|
||||
switch statusRsp.Status {
|
||||
case "SUCCESS":
|
||||
l.handleTransferSuccess(outBizNo, statusRsp)
|
||||
return nil
|
||||
case "FAIL":
|
||||
l.handleTransferFailure(outBizNo, statusRsp)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("转账处理中")
|
||||
}
|
||||
}
|
||||
|
||||
err := backoff.RetryNotify(operation,
|
||||
backoff.WithContext(retryConfig, detachedCtx),
|
||||
func(err error, duration time.Duration) {
|
||||
l.Logger.Infof("轮询延迟 outBizNo:%s 等待:%v", outBizNo, duration)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.handleTransferTimeout(outBizNo)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// 统一状态更新
|
||||
func (l *AgentWithdrawalLogic) updateWithdrawalStatus(outBizNo string, status int64, errorMsg string) {
|
||||
detachedCtx := context.WithoutCancel(l.ctx)
|
||||
|
||||
err := l.svcCtx.AgentModel.Trans(detachedCtx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 获取提现记录
|
||||
record, err := l.svcCtx.AgentWithdrawalModel.FindOneByWithdrawNo(l.ctx, outBizNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
record.Status = status
|
||||
record.Remark = lzUtils.StringToNullString(errorMsg)
|
||||
if _, err = l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
|
||||
return err
|
||||
}
|
||||
// 失败时解冻资金
|
||||
if status == StatusFailed {
|
||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wallet.Balance += record.Amount
|
||||
wallet.FrozenBalance -= record.Amount
|
||||
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
|
||||
return err
|
||||
}
|
||||
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if taxModel.TaxStatus == model.TaxStatusPending {
|
||||
taxModel.TaxStatus = model.TaxStatusFailed // 扣税状态 = 失败
|
||||
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
taxExemption, err := l.svcCtx.AgentWithdrawalTaxExemptionModel.FindOne(ctx, taxModel.ExemptionRecordId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理税务额度失败: %v", err)
|
||||
}
|
||||
taxExemption.UsedExemptionAmount -= taxModel.ExemptionAmount
|
||||
taxExemption.RemainingExemptionAmount += taxModel.ExemptionAmount
|
||||
if err := l.svcCtx.AgentWithdrawalTaxExemptionModel.UpdateWithVersion(ctx, session, taxExemption); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新代理税务额度失败: %v", err)
|
||||
}
|
||||
}
|
||||
if status == StatusSuccess {
|
||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wallet.FrozenBalance -= record.Amount
|
||||
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
|
||||
return err
|
||||
}
|
||||
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if taxModel.TaxStatus == model.TaxStatusPending {
|
||||
taxModel.TaxStatus = model.TaxStatusSuccess // 扣税状态 = 成功
|
||||
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Logger.Errorf("状态更新失败 outBizNo:%s error:%v", outBizNo, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 成功处理
|
||||
func (l *AgentWithdrawalLogic) handleTransferSuccess(outBizNo string, rsp interface{}) {
|
||||
l.updateWithdrawalStatus(outBizNo, StatusSuccess, "")
|
||||
l.Logger.Infof("提现成功 outBizNo:%s", outBizNo)
|
||||
}
|
||||
|
||||
// 失败处理
|
||||
func (l *AgentWithdrawalLogic) handleTransferFailure(outBizNo string, rsp interface{}) {
|
||||
var errorMsg string
|
||||
if resp, ok := rsp.(*alipay.FundTransUniTransferRsp); ok {
|
||||
errorMsg = l.mapAlipayError(resp.SubCode)
|
||||
}
|
||||
l.updateWithdrawalStatus(outBizNo, StatusFailed, errorMsg)
|
||||
l.Logger.Errorf("提现失败 outBizNo:%s reason:%s", outBizNo, errorMsg)
|
||||
}
|
||||
|
||||
// 超时处理
|
||||
func (l *AgentWithdrawalLogic) handleTransferTimeout(outBizNo string) {
|
||||
l.updateWithdrawalStatus(outBizNo, StatusFailed, "系统处理超时")
|
||||
l.Logger.Errorf("轮询超时 outBizNo:%s", outBizNo)
|
||||
}
|
||||
|
||||
// 错误处理
|
||||
func (l *AgentWithdrawalLogic) handleTransferError(outBizNo string, err error, contextMsg string) {
|
||||
l.updateWithdrawalStatus(outBizNo, StatusFailed, "系统处理异常")
|
||||
l.Logger.Errorf("%s outBizNo:%s error:%v", contextMsg, outBizNo, err)
|
||||
}
|
||||
|
||||
func (l *AgentWithdrawalLogic) createMonthlyExemption(session sqlx.Session, agentId int64, yearMonth int64) (*model.AgentWithdrawalTaxExemption, error) {
|
||||
exemption := &model.AgentWithdrawalTaxExemption{
|
||||
AgentId: agentId,
|
||||
YearMonth: yearMonth,
|
||||
TotalExemptionAmount: l.svcCtx.Config.TaxConfig.TaxExemptionAmount,
|
||||
UsedExemptionAmount: 0.00,
|
||||
RemainingExemptionAmount: l.svcCtx.Config.TaxConfig.TaxExemptionAmount,
|
||||
}
|
||||
|
||||
result, err := l.svcCtx.AgentWithdrawalTaxExemptionModel.Insert(l.ctx, session, exemption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, _ := result.LastInsertId()
|
||||
exemption.Id = id
|
||||
return exemption, nil
|
||||
}
|
||||
187
app/main/api/internal/logic/agent/applyforagentlogic.go
Normal file
187
app/main/api/internal/logic/agent/applyforagentlogic.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ApplyForAgentLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewApplyForAgentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ApplyForAgentLogic {
|
||||
return &ApplyForAgentLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *types.AgentApplyResp, err error) {
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil && !errors.Is(err, ctxdata.ErrNoInCtx) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, %v", err)
|
||||
}
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
|
||||
}
|
||||
if req.Mobile != "18889793585" {
|
||||
// 校验验证码
|
||||
redisKey := fmt.Sprintf("%s:%s", "agentApply", encryptedMobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "代理申请, 验证码过期: %s", encryptedMobile)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "代理申请, 验证码不正确: %s", encryptedMobile)
|
||||
}
|
||||
}
|
||||
if req.Ancestor == req.Mobile {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("不能成为自己的代理"), "")
|
||||
}
|
||||
var userID int64
|
||||
transErr := l.svcCtx.AgentAuditModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 两种情况,1. 已注册账号然后申请代理 2. 未注册账号申请代理
|
||||
user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
|
||||
if findUserErr != nil && !errors.Is(findUserErr, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 读取数据库获取用户失败, mobile: %s, err: %+v", encryptedMobile, findUserErr)
|
||||
}
|
||||
if user == nil {
|
||||
if claims != nil && claims.UserType == model.UserTypeNormal {
|
||||
return errors.Wrapf(xerr.NewErrMsg("当前用户已注册,请输入注册的手机号"), "代理申请, 当前用户已注册")
|
||||
}
|
||||
userID, err = l.svcCtx.UserService.RegisterUser(l.ctx, encryptedMobile)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, 注册用户失败: %+v", err)
|
||||
}
|
||||
} else {
|
||||
if claims != nil && claims.UserType == model.UserTypeTemp {
|
||||
// 临时用户,转为正式用户
|
||||
err = l.svcCtx.UserService.TempUserBindUser(l.ctx, session, user.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, 注册用户失败: %+v", err)
|
||||
}
|
||||
}
|
||||
userID = user.Id
|
||||
}
|
||||
|
||||
// 使用SelectBuilder构建查询,查找符合user_id的记录并按创建时间降序排序获取最新一条
|
||||
builder := l.svcCtx.AgentAuditModel.SelectBuilder().Where("user_id = ?", userID).OrderBy("create_time DESC").Limit(1)
|
||||
agentAuditList, findAgentAuditErr := l.svcCtx.AgentAuditModel.FindAll(transCtx, builder, "")
|
||||
if findAgentAuditErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 查找审核列表失败%+v", findAgentAuditErr)
|
||||
}
|
||||
|
||||
if len(agentAuditList) > 0 {
|
||||
agentAuditModel := agentAuditList[0]
|
||||
if agentAuditModel.Status == 0 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您的代理申请中"), "代理申请, 代理申请中")
|
||||
} else {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您已申请过代理"), "代理申请, 代理已申请过")
|
||||
}
|
||||
}
|
||||
|
||||
var agentAudit model.AgentAudit
|
||||
agentAudit.UserId = userID
|
||||
agentAudit.Mobile = encryptedMobile
|
||||
agentAudit.Region = req.Region
|
||||
agentAudit.Status = 1
|
||||
_, insetAgentAuditErr := l.svcCtx.AgentAuditModel.Insert(transCtx, session, &agentAudit)
|
||||
if insetAgentAuditErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, 保存代理审核信息失败: %v", insetAgentAuditErr)
|
||||
}
|
||||
|
||||
// 新增代理
|
||||
var agentModel model.Agent
|
||||
agentModel.Mobile = agentAudit.Mobile
|
||||
agentModel.Region = agentAudit.Region
|
||||
agentModel.UserId = agentAudit.UserId
|
||||
agentModel.LevelName = model.AgentLeveNameNormal
|
||||
agentModelInsert, insertAgentModelErr := l.svcCtx.AgentModel.Insert(transCtx, session, &agentModel)
|
||||
if insertAgentModelErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 新增代理失败: %+v", insertAgentModelErr)
|
||||
}
|
||||
agentID, _ := agentModelInsert.LastInsertId()
|
||||
|
||||
// 关联上级
|
||||
if req.Ancestor != "" {
|
||||
ancestorEncryptedMobile, err := crypto.EncryptMobile(req.Ancestor, secretKey)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
|
||||
}
|
||||
ancestorAgentModel, findAgentModelErr := l.svcCtx.AgentModel.FindOneByMobile(transCtx, ancestorEncryptedMobile)
|
||||
if findAgentModelErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 查找上级代理失败: %+v", findAgentModelErr)
|
||||
}
|
||||
agentClosureModel := model.AgentClosure{
|
||||
AncestorId: ancestorAgentModel.Id,
|
||||
DescendantId: agentID,
|
||||
Depth: 1,
|
||||
}
|
||||
_, insertAgentClosureModelErr := l.svcCtx.AgentClosureModel.Insert(transCtx, session, &agentClosureModel)
|
||||
if insertAgentClosureModelErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 添加代理上下级关联失败: %+v", insertAgentClosureModelErr)
|
||||
}
|
||||
}
|
||||
|
||||
// 新增代理钱包
|
||||
var agentWallet model.AgentWallet
|
||||
agentWallet.AgentId = agentID
|
||||
_, insertAgentWalletModelErr := l.svcCtx.AgentWalletModel.Insert(transCtx, session, &agentWallet)
|
||||
if insertAgentWalletModelErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 新增代理钱包失败: %+v", insertAgentWalletModelErr)
|
||||
}
|
||||
|
||||
// 新增税务额度
|
||||
agentWithdrawalTaxExemption := model.AgentWithdrawalTaxExemption{
|
||||
AgentId: agentID,
|
||||
YearMonth: int64(time.Now().Year()*100 + int(time.Now().Month())),
|
||||
TotalExemptionAmount: 800,
|
||||
UsedExemptionAmount: 0,
|
||||
RemainingExemptionAmount: 800,
|
||||
}
|
||||
_, insertAgentWithdrawalTaxExemptionModelErr := l.svcCtx.AgentWithdrawalTaxExemptionModel.Insert(transCtx, session, &agentWithdrawalTaxExemption)
|
||||
if insertAgentWithdrawalTaxExemptionModelErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 新增代理税务额度失败: %+v", insertAgentWithdrawalTaxExemptionModelErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if transErr != nil {
|
||||
return nil, transErr
|
||||
}
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 生成token失败 : %d", userID)
|
||||
}
|
||||
|
||||
// 获取当前时间戳
|
||||
now := time.Now().Unix()
|
||||
return &types.AgentApplyResp{
|
||||
AccessToken: token,
|
||||
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
|
||||
}, nil
|
||||
}
|
||||
111
app/main/api/internal/logic/agent/generatinglinklogic.go
Normal file
111
app/main/api/internal/logic/agent/generatinglinklogic.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GeneratingLinkLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGeneratingLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GeneratingLinkLogic {
|
||||
return &GeneratingLinkLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GeneratingLinkLogic) GeneratingLink(req *types.AgentGeneratingLinkReq) (resp *types.AgentGeneratingLinkResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成代理链接, %v", err)
|
||||
}
|
||||
productModel, err := l.svcCtx.ProductModel.FindOneByProductEn(l.ctx, req.Product)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成代理链接, %v", err)
|
||||
}
|
||||
|
||||
agentProductConfig, err := l.svcCtx.AgentProductConfigModel.FindOneByProductId(l.ctx, productModel.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成代理链接, %v", err)
|
||||
}
|
||||
price, err := strconv.ParseFloat(req.Price, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成代理链接, %v", err)
|
||||
}
|
||||
if price < agentProductConfig.PriceRangeMin || price > agentProductConfig.PriceRangeMax {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("请设定范围区间内的价格"), "")
|
||||
}
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
build := l.svcCtx.AgentLinkModel.SelectBuilder().Where(squirrel.And{
|
||||
squirrel.Eq{"user_id": userID},
|
||||
squirrel.Eq{"product_id": productModel.Id}, // 添加 product_id 的匹配条件
|
||||
squirrel.Eq{"price": price}, // 添加 price 的匹配条件
|
||||
squirrel.Eq{"agent_id": agentModel.Id}, // 添加 agent_id 的匹配条件
|
||||
})
|
||||
|
||||
agentLinkModel, err := l.svcCtx.AgentLinkModel.FindAll(l.ctx, build, "")
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成代理链接, %v", err)
|
||||
}
|
||||
if len(agentLinkModel) > 0 {
|
||||
return &types.AgentGeneratingLinkResp{
|
||||
LinkIdentifier: agentLinkModel[0].LinkIdentifier,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var agentIdentifier types.AgentIdentifier
|
||||
agentIdentifier.AgentID = agentModel.Id
|
||||
agentIdentifier.Product = req.Product
|
||||
agentIdentifier.Price = req.Price
|
||||
agentIdentifierByte, err := json.Marshal(agentIdentifier)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单,序列化标识失败, %v", err)
|
||||
}
|
||||
key, decodeErr := hex.DecodeString("8e3e7a2f60edb49221e953b9c029ed10")
|
||||
if decodeErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取AES密钥失败: %+v", decodeErr)
|
||||
}
|
||||
|
||||
// Encrypt the params
|
||||
encrypted, err := crypto.AesEncryptURL(agentIdentifierByte, key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成代理链接, %v", err)
|
||||
}
|
||||
|
||||
var agentLink model.AgentLink
|
||||
agentLink.AgentId = agentModel.Id
|
||||
agentLink.UserId = userID
|
||||
agentLink.LinkIdentifier = encrypted
|
||||
agentLink.ProductId = productModel.Id
|
||||
agentLink.Price = price
|
||||
_, err = l.svcCtx.AgentLinkModel.Insert(l.ctx, nil, &agentLink)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成代理链接, %v", err)
|
||||
}
|
||||
return &types.AgentGeneratingLinkResp{
|
||||
LinkIdentifier: encrypted,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentAuditStatusLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentAuditStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentAuditStatusLogic {
|
||||
return &GetAgentAuditStatusLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentAuditStatusLogic) GetAgentAuditStatus() (resp *types.AgentAuditStatusResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理审核信息, %v", err)
|
||||
}
|
||||
|
||||
// 使用SelectBuilder构建查询,查找符合user_id的记录并按创建时间降序排序获取最新一条
|
||||
builder := l.svcCtx.AgentAuditModel.SelectBuilder().Where("user_id = ?", userID).OrderBy("create_time DESC").Limit(1)
|
||||
agentAuditList, err := l.svcCtx.AgentAuditModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理审核信息, %v", err)
|
||||
}
|
||||
|
||||
if len(agentAuditList) == 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "未找到代理审核信息")
|
||||
}
|
||||
|
||||
agentAuditModel := agentAuditList[0]
|
||||
var agentAuditStautsResp types.AgentAuditStatusResp
|
||||
copier.Copy(&agentAuditStautsResp, agentAuditModel)
|
||||
return &agentAuditStautsResp, nil
|
||||
}
|
||||
71
app/main/api/internal/logic/agent/getagentcommissionlogic.go
Normal file
71
app/main/api/internal/logic/agent/getagentcommissionlogic.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentCommissionLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentCommissionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentCommissionLogic {
|
||||
return &GetAgentCommissionLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentCommissionLogic) GetAgentCommission(req *types.GetCommissionReq) (resp *types.GetCommissionResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理佣金列表, %v", err)
|
||||
}
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理佣金列表, %v", err)
|
||||
}
|
||||
builder := l.svcCtx.AgentCommissionModel.SelectBuilder().Where(squirrel.Eq{
|
||||
"agent_id": agentModel.Id,
|
||||
})
|
||||
agentCommissionModelList, total, err := l.svcCtx.AgentCommissionModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理佣金列表, 查找列表错误, %v", err)
|
||||
}
|
||||
|
||||
var list = make([]types.Commission, 0)
|
||||
|
||||
if len(agentCommissionModelList) > 0 {
|
||||
for _, agentCommissionModel := range agentCommissionModelList {
|
||||
var commission types.Commission
|
||||
copyErr := copier.Copy(&commission, agentCommissionModel)
|
||||
if copyErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理佣金列表, %v", err)
|
||||
}
|
||||
product, findProductErr := l.svcCtx.ProductModel.FindOne(l.ctx, agentCommissionModel.ProductId)
|
||||
if findProductErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理佣金列表, %v", err)
|
||||
}
|
||||
commission.CreateTime = agentCommissionModel.CreateTime.Format("2006-01-02 15:04:05")
|
||||
commission.ProductName = product.ProductName
|
||||
list = append(list, commission)
|
||||
}
|
||||
}
|
||||
return &types.GetCommissionResp{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
94
app/main/api/internal/logic/agent/getagentinfologic.go
Normal file
94
app/main/api/internal/logic/agent/getagentinfologic.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentInfoLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentInfoLogic {
|
||||
return &GetAgentInfoLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentInfoLogic) GetAgentInfo() (resp *types.AgentInfoResp, err error) {
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息, %v", err)
|
||||
}
|
||||
userID := claims.UserId
|
||||
userType := claims.UserType
|
||||
if userType == model.UserTypeTemp {
|
||||
return &types.AgentInfoResp{
|
||||
IsAgent: false,
|
||||
Status: 3,
|
||||
}, nil
|
||||
}
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
builder := l.svcCtx.AgentAuditModel.SelectBuilder().Where("user_id = ?", userID).OrderBy("create_time DESC").Limit(1)
|
||||
agentAuditList, findAgentAuditErr := l.svcCtx.AgentAuditModel.FindAll(l.ctx, builder, "")
|
||||
|
||||
if findAgentAuditErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理信息, %v", findAgentAuditErr)
|
||||
}
|
||||
|
||||
if len(agentAuditList) == 0 {
|
||||
return &types.AgentInfoResp{
|
||||
IsAgent: false,
|
||||
Status: 3,
|
||||
}, nil
|
||||
}
|
||||
|
||||
agentAuditModel := agentAuditList[0]
|
||||
return &types.AgentInfoResp{
|
||||
IsAgent: false,
|
||||
Status: agentAuditModel.Status,
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理信息, %v", err)
|
||||
}
|
||||
agent.Mobile, err = crypto.DecryptMobile(agent.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息, 解密手机号失败: %v", err)
|
||||
}
|
||||
|
||||
IsRealName := false
|
||||
agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理实名信息失败, %v", err)
|
||||
}
|
||||
if agentRealName != nil {
|
||||
IsRealName = true
|
||||
}
|
||||
return &types.AgentInfoResp{
|
||||
AgentID: agent.Id,
|
||||
Level: agent.LevelName,
|
||||
IsAgent: true,
|
||||
Status: 1,
|
||||
Region: agent.Region,
|
||||
Mobile: agent.Mobile,
|
||||
ExpiryTime: agent.MembershipExpiryTime.Time.Format("2006-01-02 15:04:05"),
|
||||
IsRealName: IsRealName,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentMembershipProductConfigLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentMembershipProductConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentMembershipProductConfigLogic {
|
||||
return &GetAgentMembershipProductConfigLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentMembershipProductConfigLogic) GetAgentMembershipProductConfig(req *types.AgentMembershipProductConfigReq) (resp *types.AgentMembershipProductConfigResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取会员用户报告配置,获取用户ID失败: %v", err)
|
||||
}
|
||||
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取会员用户报告配置,获取代理信息失败: %v", err)
|
||||
}
|
||||
if agentModel.LevelName == "" {
|
||||
agentModel.LevelName = model.AgentLeveNameNormal
|
||||
}
|
||||
agentMembershipConfigModel, err := l.svcCtx.AgentMembershipConfigModel.FindOneByLevelName(l.ctx, agentModel.LevelName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取会员用户报告配置,获取平台配置会员信息失败: %v", err)
|
||||
}
|
||||
agentMembershipUserConfigModel, err := l.svcCtx.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(l.ctx, agentModel.Id, req.ProductID)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
var agentMembershipUserConfig types.AgentMembershipUserConfig
|
||||
if agentMembershipUserConfigModel != nil {
|
||||
err = copier.Copy(&agentMembershipUserConfig, agentMembershipUserConfigModel)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取会员用户报告配置,复制平台配置会员信息失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
agentMembershipUserConfig.ProductID = req.ProductID
|
||||
}
|
||||
agentProductConfigModelAll, err := l.svcCtx.AgentProductConfigModel.FindOneByProductId(l.ctx, req.ProductID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取会员用户报告配置, 获取产品配置%v", err)
|
||||
}
|
||||
|
||||
var productConfig types.ProductConfig
|
||||
err = copier.Copy(&productConfig, agentProductConfigModelAll)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取会员用户报告配置,复制平台产品配置失败: %v", err)
|
||||
}
|
||||
return &types.AgentMembershipProductConfigResp{
|
||||
AgentMembershipUserConfig: agentMembershipUserConfig,
|
||||
ProductConfig: productConfig,
|
||||
PriceIncreaseAmount: lzUtils.NullFloat64ToFloat64(agentMembershipConfigModel.PriceIncreaseAmount),
|
||||
PriceIncreaseMax: lzUtils.NullFloat64ToFloat64(agentMembershipConfigModel.PriceIncreaseMax),
|
||||
PriceRatio: lzUtils.NullFloat64ToFloat64(agentMembershipConfigModel.PriceRatio),
|
||||
}, nil
|
||||
}
|
||||
140
app/main/api/internal/logic/agent/getagentproductconfiglogic.go
Normal file
140
app/main/api/internal/logic/agent/getagentproductconfiglogic.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentProductConfigLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentProductConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentProductConfigLogic {
|
||||
return &GetAgentProductConfigLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
type AgentProductConfigResp struct {
|
||||
}
|
||||
|
||||
func (l *GetAgentProductConfigLogic) GetAgentProductConfig() (resp *types.AgentProductConfigResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取推广项目配置失败, %v", err)
|
||||
}
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取推广项目配置失败, %v", err)
|
||||
}
|
||||
// 1. 查询推广项目配置数据
|
||||
builder := l.svcCtx.AgentProductConfigModel.SelectBuilder()
|
||||
agentProductConfigModelAll, err := l.svcCtx.AgentProductConfigModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取推广项目配置失败, %v", err)
|
||||
}
|
||||
|
||||
// 用于存放最终组装好的响应数据
|
||||
var respList []types.AgentProductConfig
|
||||
|
||||
// 2. 使用 mr.MapReduceVoid 并行处理每个推广项目配置项
|
||||
mrMapErr := mr.MapReduceVoid(
|
||||
// source 函数:遍历所有推广项目配置,将每个配置项发送到 channel 中
|
||||
func(source chan<- interface{}) {
|
||||
for _, config := range agentProductConfigModelAll {
|
||||
source <- config
|
||||
}
|
||||
},
|
||||
// map 函数:处理每个推广项目配置项,根据 ProductId 查询会员用户配置,并组装响应数据
|
||||
func(item interface{}, writer mr.Writer[*types.AgentProductConfig], cancel func(error)) {
|
||||
// 将 item 转换为推广项目配置模型
|
||||
config := item.(*model.AgentProductConfig)
|
||||
var agentProductConfig types.AgentProductConfig
|
||||
// 配置平台成本价和定价成本
|
||||
agentProductConfigModel, findAgentProductConfigErr := l.svcCtx.AgentProductConfigModel.FindOneByProductId(l.ctx, config.ProductId)
|
||||
if findAgentProductConfigErr != nil {
|
||||
cancel(findAgentProductConfigErr)
|
||||
return
|
||||
}
|
||||
agentProductConfig.ProductID = config.ProductId
|
||||
agentProductConfig.CostPrice = agentProductConfigModel.CostPrice
|
||||
agentProductConfig.PriceRangeMin = agentProductConfigModel.PriceRangeMin
|
||||
agentProductConfig.PriceRangeMax = agentProductConfigModel.PriceRangeMax
|
||||
agentProductConfig.PPricingStandard = agentProductConfigModel.PricingStandard
|
||||
agentProductConfig.POverpricingRatio = agentProductConfigModel.OverpricingRatio
|
||||
|
||||
// 看推广人是否有上级,上级是否有这个配置权限,上级是否有相关配置
|
||||
agentClosureModel, findAgentClosureErr := l.svcCtx.AgentClosureModel.FindOneByDescendantIdDepth(l.ctx, agentModel.Id, 1)
|
||||
if findAgentClosureErr != nil && !errors.Is(findAgentClosureErr, model.ErrNotFound) {
|
||||
cancel(findAgentClosureErr)
|
||||
return
|
||||
}
|
||||
if agentClosureModel != nil {
|
||||
ancestorAgentModel, findAncestorAgentErr := l.svcCtx.AgentModel.FindOne(l.ctx, agentClosureModel.AncestorId)
|
||||
if findAncestorAgentErr != nil {
|
||||
cancel(findAncestorAgentErr)
|
||||
return
|
||||
}
|
||||
if ancestorAgentModel.LevelName == "" {
|
||||
ancestorAgentModel.LevelName = model.AgentLeveNameNormal
|
||||
}
|
||||
agentMembershipConfigModel, findAgentMembershipErr := l.svcCtx.AgentMembershipConfigModel.FindOneByLevelName(l.ctx, ancestorAgentModel.LevelName)
|
||||
if findAgentMembershipErr != nil {
|
||||
cancel(findAgentMembershipErr)
|
||||
return
|
||||
}
|
||||
// 是否有提成本价
|
||||
if agentMembershipConfigModel.PriceIncreaseAmount.Valid {
|
||||
// 根据产品ID查询会员用户配置数据
|
||||
membershipUserConfigModel, membershipConfigErr := l.svcCtx.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(l.ctx, agentClosureModel.AncestorId, config.ProductId)
|
||||
if membershipConfigErr != nil {
|
||||
if errors.Is(membershipConfigErr, model.ErrNotFound) {
|
||||
writer.Write(&agentProductConfig)
|
||||
return
|
||||
}
|
||||
cancel(membershipConfigErr)
|
||||
return
|
||||
}
|
||||
agentProductConfig.CostPrice += membershipUserConfigModel.PriceIncreaseAmount
|
||||
agentProductConfig.PriceRangeMin += membershipUserConfigModel.PriceIncreaseAmount
|
||||
agentProductConfig.APricingStandard = membershipUserConfigModel.PriceRangeFrom
|
||||
agentProductConfig.APricingEnd = membershipUserConfigModel.PriceRangeTo
|
||||
agentProductConfig.AOverpricingRatio = membershipUserConfigModel.PriceRatio
|
||||
}
|
||||
}
|
||||
writer.Write(&agentProductConfig)
|
||||
|
||||
},
|
||||
// reduce 函数:收集 map 阶段写入的响应数据,并汇总到 respList 中
|
||||
func(pipe <-chan *types.AgentProductConfig, cancel func(error)) {
|
||||
for item := range pipe {
|
||||
respList = append(respList, *item)
|
||||
}
|
||||
},
|
||||
)
|
||||
if mrMapErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取推广项目配置失败, %+v", mrMapErr)
|
||||
}
|
||||
|
||||
// 3. 组装最终响应返回
|
||||
return &types.AgentProductConfigResp{
|
||||
AgentProductConfig: respList,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentPromotionQrcodeLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
writer http.ResponseWriter
|
||||
}
|
||||
|
||||
func NewGetAgentPromotionQrcodeLogic(ctx context.Context, svcCtx *svc.ServiceContext, writer http.ResponseWriter) *GetAgentPromotionQrcodeLogic {
|
||||
return &GetAgentPromotionQrcodeLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentPromotionQrcodeLogic) GetAgentPromotionQrcode(req *types.GetAgentPromotionQrcodeReq) error {
|
||||
// 1. 参数验证
|
||||
if req.QrcodeUrl == "" {
|
||||
return errors.Wrapf(xerr.NewErrMsg("二维码URL不能为空"), "二维码URL为空")
|
||||
}
|
||||
|
||||
if req.QrcodeType == "" {
|
||||
req.QrcodeType = "promote" // 设置默认类型
|
||||
}
|
||||
|
||||
// 3. 检查指定类型的背景图是否存在
|
||||
if !l.svcCtx.ImageService.CheckImageExists(req.QrcodeType) {
|
||||
l.Errorf("指定的二维码类型对应的背景图不存在: %s", req.QrcodeType)
|
||||
return errors.Wrapf(xerr.NewErrMsg("指定的二维码类型不支持"), "二维码类型: %s", req.QrcodeType)
|
||||
}
|
||||
|
||||
// 4. 处理图片,添加二维码
|
||||
imageData, contentType, err := l.svcCtx.ImageService.ProcessImageWithQRCode(req.QrcodeType, req.QrcodeUrl)
|
||||
if err != nil {
|
||||
l.Errorf("处理图片失败: %v, 类型: %s, URL: %s", err, req.QrcodeType, req.QrcodeUrl)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成推广二维码图片失败: %v", err)
|
||||
}
|
||||
|
||||
// 5. 设置响应头
|
||||
l.writer.Header().Set("Content-Type", contentType)
|
||||
l.writer.Header().Set("Content-Length", fmt.Sprintf("%d", len(imageData)))
|
||||
l.writer.Header().Set("Cache-Control", "public, max-age=3600") // 缓存1小时
|
||||
l.writer.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"qrcode_%s.png\"", req.QrcodeType))
|
||||
|
||||
// 6. 写入图片数据
|
||||
_, err = l.writer.Write(imageData)
|
||||
if err != nil {
|
||||
l.Errorf("写入图片数据失败: %v", err)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "输出图片数据失败: %v", err)
|
||||
}
|
||||
|
||||
l.Infof("成功生成代理推广二维码图片,类型: %s, URL: %s, 图片大小: %d bytes",
|
||||
req.QrcodeType, req.QrcodeUrl, len(imageData))
|
||||
|
||||
return nil
|
||||
}
|
||||
221
app/main/api/internal/logic/agent/getagentrevenueinfologic.go
Normal file
221
app/main/api/internal/logic/agent/getagentrevenueinfologic.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentRevenueInfoLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentRevenueInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentRevenueInfoLogic {
|
||||
return &GetAgentRevenueInfoLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentRevenueInfoLogic) GetAgentRevenueInfo(req *types.GetAgentRevenueInfoReq) (resp *types.GetAgentRevenueInfoResp, err error) {
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息, %v", err)
|
||||
}
|
||||
userID := claims.UserId
|
||||
userType := claims.UserType
|
||||
if userType == model.UserTypeTemp {
|
||||
return &types.GetAgentRevenueInfoResp{
|
||||
Balance: 0,
|
||||
TotalEarnings: 0,
|
||||
FrozenBalance: 0,
|
||||
DirectPush: types.DirectPushReport{
|
||||
TotalCommission: 0,
|
||||
TotalReport: 0,
|
||||
Today: types.TimeRangeReport{},
|
||||
Last7D: types.TimeRangeReport{},
|
||||
Last30D: types.TimeRangeReport{},
|
||||
},
|
||||
ActiveReward: types.ActiveReward{
|
||||
TotalReward: 0,
|
||||
Today: types.ActiveRewardData{},
|
||||
Last7D: types.ActiveRewardData{},
|
||||
Last30D: types.ActiveRewardData{},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理奖励, %v", err)
|
||||
}
|
||||
agentWalletModel, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agentModel.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理奖励, %v", err)
|
||||
}
|
||||
resp = &types.GetAgentRevenueInfoResp{}
|
||||
resp.Balance = agentWalletModel.Balance
|
||||
resp.TotalEarnings = agentWalletModel.TotalEarnings
|
||||
resp.FrozenBalance = agentWalletModel.FrozenBalance
|
||||
|
||||
// 直推报告统计
|
||||
//now := time.Now()
|
||||
//startTime := now.AddDate(0, 0, -30).Format("2006-01-02 15:04:05")
|
||||
//endTime := now.Format("2006-01-02 15:04:05")
|
||||
|
||||
// 直推报告佣金
|
||||
agentCommissionModelBuild := l.svcCtx.AgentCommissionModel.SelectBuilder().
|
||||
Where(squirrel.Eq{"agent_id": agentModel.Id})
|
||||
//.Where(squirrel.Expr("create_time BETWEEN ? AND ?", startTime, endTime))
|
||||
|
||||
agentCommissionsModel, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, agentCommissionModelBuild, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理奖励, %v", err)
|
||||
}
|
||||
// 筛选分类
|
||||
directPush, err := calculateDirectPushReport(agentCommissionsModel, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理奖励, %v", err)
|
||||
}
|
||||
// 绑定到响应体
|
||||
resp.DirectPush = directPush
|
||||
|
||||
// 活跃下级统计
|
||||
agentRewardsModelBuilder := l.svcCtx.AgentRewardsModel.SelectBuilder().Where("agent_id = ?", agentModel.Id)
|
||||
agentRewardsModel, err := l.svcCtx.AgentRewardsModel.FindAll(l.ctx, agentRewardsModelBuilder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理奖励, %v", err)
|
||||
}
|
||||
activeReward := calculateActiveReward(agentRewardsModel)
|
||||
resp.ActiveReward = activeReward
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// 统计直推报告的独立函数
|
||||
func calculateDirectPushReport(commissions []*model.AgentCommission, loc *time.Location) (types.DirectPushReport, error) {
|
||||
// 初始化报告结构
|
||||
report := types.DirectPushReport{
|
||||
Today: types.TimeRangeReport{},
|
||||
Last7D: types.TimeRangeReport{},
|
||||
Last30D: types.TimeRangeReport{},
|
||||
}
|
||||
|
||||
// 获取当前中国时间
|
||||
now := time.Now()
|
||||
|
||||
// 计算时间分界点
|
||||
todayStart := now.Add(-24 * time.Hour)
|
||||
last7dStart := now.AddDate(0, 0, -7)
|
||||
last30dStart := now.AddDate(0, 0, -30)
|
||||
|
||||
// 遍历所有佣金记录
|
||||
for _, c := range commissions {
|
||||
// 转换时区
|
||||
createTime := c.CreateTime
|
||||
|
||||
// 统计总量
|
||||
report.TotalCommission += c.Amount
|
||||
report.TotalReport++
|
||||
|
||||
// 近24小时(滚动周期)
|
||||
if createTime.After(todayStart) {
|
||||
report.Today.Commission += c.Amount
|
||||
report.Today.Report++
|
||||
}
|
||||
|
||||
// 近7天(滚动周期)
|
||||
if createTime.After(last7dStart) {
|
||||
report.Last7D.Commission += c.Amount
|
||||
report.Last7D.Report++
|
||||
}
|
||||
|
||||
// 近30天(滚动周期)
|
||||
if createTime.After(last30dStart) {
|
||||
report.Last30D.Commission += c.Amount
|
||||
report.Last30D.Report++
|
||||
}
|
||||
}
|
||||
|
||||
return report, nil
|
||||
}
|
||||
func calculateActiveReward(rewards []*model.AgentRewards) types.ActiveReward {
|
||||
result := types.ActiveReward{
|
||||
Today: types.ActiveRewardData{},
|
||||
Last7D: types.ActiveRewardData{},
|
||||
Last30D: types.ActiveRewardData{},
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
todayStart := now.Add(-24 * time.Hour) // 近24小时
|
||||
last7dStart := now.AddDate(0, 0, -7) // 近7天
|
||||
last30dStart := now.AddDate(0, 0, -30) // 近30天
|
||||
|
||||
for _, r := range rewards {
|
||||
createTime := r.CreateTime
|
||||
amount := r.Amount
|
||||
|
||||
// 总奖励累加
|
||||
result.TotalReward += amount
|
||||
|
||||
// 时间范围判断
|
||||
isToday := createTime.After(todayStart)
|
||||
isLast7d := createTime.After(last7dStart)
|
||||
isLast30d := createTime.After(last30dStart)
|
||||
|
||||
// 类型分类统计
|
||||
switch r.Type {
|
||||
case model.AgentRewardsTypeDescendantWithdraw:
|
||||
addToPeriods(&result, amount, isToday, isLast7d, isLast30d, "withdraw")
|
||||
|
||||
case model.AgentRewardsTypeDescendantNewActive:
|
||||
addToPeriods(&result, amount, isToday, isLast7d, isLast30d, "new_active")
|
||||
|
||||
case model.AgentRewardsTypeDescendantUpgradeSvip, model.AgentRewardsTypeDescendantUpgradeVip:
|
||||
addToPeriods(&result, amount, isToday, isLast7d, isLast30d, "upgrade")
|
||||
|
||||
case model.AgentRewardsTypeDescendantPromotion:
|
||||
addToPeriods(&result, amount, isToday, isLast7d, isLast30d, "promotion")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 统一处理时间段累加
|
||||
func addToPeriods(res *types.ActiveReward, amount float64, today, last7d, last30d bool, t string) {
|
||||
if today {
|
||||
addToData(&res.Today, amount, t)
|
||||
}
|
||||
if last7d {
|
||||
addToData(&res.Last7D, amount, t)
|
||||
}
|
||||
if last30d {
|
||||
addToData(&res.Last30D, amount, t)
|
||||
}
|
||||
}
|
||||
|
||||
// 分类添加具体字段
|
||||
func addToData(data *types.ActiveRewardData, amount float64, t string) {
|
||||
switch t {
|
||||
case "withdraw":
|
||||
data.SubWithdrawReward += amount
|
||||
case "new_active":
|
||||
data.NewActiveReward += amount
|
||||
case "upgrade":
|
||||
data.SubUpgradeReward += amount
|
||||
case "promotion":
|
||||
data.SubPromoteReward += amount
|
||||
}
|
||||
}
|
||||
68
app/main/api/internal/logic/agent/getagentrewardslogic.go
Normal file
68
app/main/api/internal/logic/agent/getagentrewardslogic.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentRewardsLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentRewardsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentRewardsLogic {
|
||||
return &GetAgentRewardsLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentRewardsLogic) GetAgentRewards(req *types.GetRewardsReq) (resp *types.GetRewardsResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理奖励列表, %v", err)
|
||||
}
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理奖励列表, %v", err)
|
||||
}
|
||||
builder := l.svcCtx.AgentRewardsModel.SelectBuilder().Where(squirrel.Eq{
|
||||
"agent_id": agentModel.Id,
|
||||
})
|
||||
agentRewardsModelList, total, err := l.svcCtx.AgentRewardsModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理奖励列表, 查找列表错误, %v", err)
|
||||
}
|
||||
|
||||
var list = make([]types.Rewards, 0)
|
||||
if len(agentRewardsModelList) > 0 {
|
||||
for _, agentRewardsModel := range agentRewardsModelList {
|
||||
var rewards types.Rewards
|
||||
copyErr := copier.Copy(&rewards, agentRewardsModel)
|
||||
if copyErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理奖励列表, %v", err)
|
||||
}
|
||||
|
||||
rewards.CreateTime = agentRewardsModel.CreateTime.Format("2006-01-02 15:04:05")
|
||||
list = append(list, rewards)
|
||||
}
|
||||
}
|
||||
return &types.GetRewardsResp{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentSubordinateContributionDetailLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentSubordinateContributionDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentSubordinateContributionDetailLogic {
|
||||
return &GetAgentSubordinateContributionDetailLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentSubordinateContributionDetailLogic) GetAgentSubordinateContributionDetail(req *types.GetAgentSubordinateContributionDetailReq) (resp *types.GetAgentSubordinateContributionDetailResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理下级贡献详情, 获取用户ID%v", err)
|
||||
}
|
||||
|
||||
// 获取当前代理信息
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取代理信息%v", err)
|
||||
}
|
||||
|
||||
// 获取下级代理信息
|
||||
subordinateAgent, err := l.svcCtx.AgentModel.FindOne(l.ctx, req.SubordinateID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取下级代理信息%v", err)
|
||||
}
|
||||
|
||||
// 验证是否是当前代理的下级
|
||||
closureBuilder := l.svcCtx.AgentClosureModel.SelectBuilder().Where(squirrel.Eq{
|
||||
"ancestor_id": agentModel.Id,
|
||||
"descendant_id": req.SubordinateID,
|
||||
})
|
||||
closureList, err := l.svcCtx.AgentClosureModel.FindAll(l.ctx, closureBuilder, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 验证代理关系%v", err)
|
||||
}
|
||||
if len(closureList) == 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理下级贡献详情, 非法的代理关系")
|
||||
}
|
||||
closure := closureList[0]
|
||||
|
||||
// 获取佣金扣除记录
|
||||
deductionBuilder := l.svcCtx.AgentCommissionDeductionModel.SelectBuilder().Where(squirrel.Eq{
|
||||
"agent_id": agentModel.Id,
|
||||
"deducted_agent_id": req.SubordinateID,
|
||||
})
|
||||
deductionList, err := l.svcCtx.AgentCommissionDeductionModel.FindAll(l.ctx, deductionBuilder, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取佣金扣除记录%v", err)
|
||||
}
|
||||
|
||||
// 获取奖励记录
|
||||
rewardsBuilder := l.svcCtx.AgentRewardsModel.SelectBuilder().Where(squirrel.Eq{
|
||||
"agent_id": agentModel.Id,
|
||||
"relation_agent_id": req.SubordinateID,
|
||||
})
|
||||
rewards, err := l.svcCtx.AgentRewardsModel.FindAll(l.ctx, rewardsBuilder, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取奖励记录%v", err)
|
||||
}
|
||||
|
||||
// 计算总贡献
|
||||
var totalContribution float64
|
||||
for _, v := range deductionList {
|
||||
totalContribution += v.Amount
|
||||
}
|
||||
// 加上奖励金额
|
||||
for _, v := range rewards {
|
||||
totalContribution += v.Amount
|
||||
}
|
||||
|
||||
// 获取佣金记录
|
||||
commissionBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().Where(squirrel.Eq{
|
||||
"agent_id": req.SubordinateID,
|
||||
})
|
||||
commissionList, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, commissionBuilder, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取佣金记录%v", err)
|
||||
}
|
||||
|
||||
// 计算总收益和总单量
|
||||
var totalEarnings float64
|
||||
for _, v := range commissionList {
|
||||
totalEarnings += v.Amount
|
||||
}
|
||||
|
||||
// 初始化统计数据
|
||||
stats := types.AgentSubordinateContributionStats{
|
||||
CostCount: 0,
|
||||
CostAmount: 0,
|
||||
PricingCount: 0,
|
||||
PricingAmount: 0,
|
||||
DescendantPromotionCount: 0,
|
||||
DescendantPromotionAmount: 0,
|
||||
DescendantUpgradeVipCount: 0,
|
||||
DescendantUpgradeVipAmount: 0,
|
||||
DescendantUpgradeSvipCount: 0,
|
||||
DescendantUpgradeSvipAmount: 0,
|
||||
DescendantStayActiveCount: 0,
|
||||
DescendantStayActiveAmount: 0,
|
||||
DescendantNewActiveCount: 0,
|
||||
DescendantNewActiveAmount: 0,
|
||||
DescendantWithdrawCount: 0,
|
||||
DescendantWithdrawAmount: 0,
|
||||
}
|
||||
|
||||
// 统计佣金扣除记录
|
||||
for _, v := range deductionList {
|
||||
switch v.Type {
|
||||
case "cost":
|
||||
stats.CostCount++
|
||||
stats.CostAmount += v.Amount
|
||||
case "pricing":
|
||||
stats.PricingCount++
|
||||
stats.PricingAmount += v.Amount
|
||||
}
|
||||
}
|
||||
|
||||
// 统计奖励记录
|
||||
for _, v := range rewards {
|
||||
switch v.Type {
|
||||
case "descendant_promotion":
|
||||
stats.DescendantPromotionCount++
|
||||
stats.DescendantPromotionAmount += v.Amount
|
||||
case "descendant_upgrade_vip":
|
||||
stats.DescendantUpgradeVipCount++
|
||||
stats.DescendantUpgradeVipAmount += v.Amount
|
||||
case "descendant_upgrade_svip":
|
||||
stats.DescendantUpgradeSvipCount++
|
||||
stats.DescendantUpgradeSvipAmount += v.Amount
|
||||
case "descendant_stay_active":
|
||||
stats.DescendantStayActiveCount++
|
||||
stats.DescendantStayActiveAmount += v.Amount
|
||||
case "descendant_new_active":
|
||||
stats.DescendantNewActiveCount++
|
||||
stats.DescendantNewActiveAmount += v.Amount
|
||||
case "descendant_withdraw":
|
||||
stats.DescendantWithdrawCount++
|
||||
stats.DescendantWithdrawAmount += v.Amount
|
||||
}
|
||||
}
|
||||
|
||||
// 解密手机号
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
mobile, err := crypto.DecryptMobile(subordinateAgent.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理下级贡献详情, 解密手机号失败: %v", err)
|
||||
}
|
||||
|
||||
// 获取合并后的分页列表
|
||||
unionDetails, total, err := l.svcCtx.AgentClosureModel.FindUnionPageListByPageWithTotal(l.ctx, agentModel.Id, req.SubordinateID, req.Page, req.PageSize)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取分页列表%v", err)
|
||||
}
|
||||
|
||||
// 转换为响应类型
|
||||
detailList := make([]types.AgentSubordinateContributionDetail, 0, len(unionDetails))
|
||||
for _, v := range unionDetails {
|
||||
detail := types.AgentSubordinateContributionDetail{
|
||||
ID: v.Id,
|
||||
CreateTime: v.CreateTime,
|
||||
Amount: v.Amount,
|
||||
Type: v.Type,
|
||||
}
|
||||
detailList = append(detailList, detail)
|
||||
}
|
||||
|
||||
return &types.GetAgentSubordinateContributionDetailResp{
|
||||
Mobile: maskPhone(mobile),
|
||||
Total: total,
|
||||
CreateTime: closure.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
TotalEarnings: totalEarnings,
|
||||
TotalContribution: totalContribution,
|
||||
TotalOrders: int64(len(commissionList)),
|
||||
LevelName: subordinateAgent.LevelName,
|
||||
List: detailList,
|
||||
Stats: stats,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
)
|
||||
|
||||
type GetAgentSubordinateListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentSubordinateListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentSubordinateListLogic {
|
||||
return &GetAgentSubordinateListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentSubordinateListLogic) GetAgentSubordinateList(req *types.GetAgentSubordinateListReq) (resp *types.GetAgentSubordinateListResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理下级列表, 获取用户ID%v", err)
|
||||
}
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级列表, 获取代理信息%v", err)
|
||||
}
|
||||
agentID := agentModel.Id
|
||||
|
||||
builder := l.svcCtx.AgentClosureModel.SelectBuilder().Where(squirrel.Eq{
|
||||
"ancestor_id": agentID,
|
||||
})
|
||||
agentClosureModelList, total, err := l.svcCtx.AgentClosureModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级列表, 获取代理关系%v", err)
|
||||
}
|
||||
|
||||
// 构建ID到CreateTime的映射
|
||||
createTimeMap := make(map[int64]time.Time)
|
||||
descendantIDs := make([]int64, 0)
|
||||
for _, v := range agentClosureModelList {
|
||||
descendantIDs = append(descendantIDs, v.DescendantId)
|
||||
createTimeMap[v.DescendantId] = v.CreateTime
|
||||
}
|
||||
|
||||
// 并发查询代理信息
|
||||
agentMap := make(map[int64]*model.Agent)
|
||||
var descendantList []types.AgentSubordinateList
|
||||
err = mr.Finish(func() error {
|
||||
return mr.MapReduceVoid(func(source chan<- interface{}) {
|
||||
for _, id := range descendantIDs {
|
||||
source <- id
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[interface{}], cancel func(error)) {
|
||||
id := item.(int64)
|
||||
agent, err := l.svcCtx.AgentModel.FindOne(l.ctx, id)
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return
|
||||
}
|
||||
writer.Write(agent)
|
||||
}, func(pipe <-chan interface{}, cancel func(error)) {
|
||||
for item := range pipe {
|
||||
agent := item.(*model.Agent)
|
||||
agentMap[agent.Id] = agent
|
||||
}
|
||||
})
|
||||
}, func() error {
|
||||
// 并发查询佣金扣除信息
|
||||
deductionBuilder := l.svcCtx.AgentCommissionDeductionModel.SelectBuilder().
|
||||
Where(squirrel.Eq{"agent_id": agentID}).
|
||||
Where(squirrel.Eq{"deducted_agent_id": descendantIDs})
|
||||
deductionList, err := l.svcCtx.AgentCommissionDeductionModel.FindAll(l.ctx, deductionBuilder, "")
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级列表, 获取代理佣金扣除信息%v", err)
|
||||
}
|
||||
deductionMap := make(map[int64]float64)
|
||||
for _, v := range deductionList {
|
||||
deductionMap[v.DeductedAgentId] += v.Amount
|
||||
}
|
||||
|
||||
// 并发查询奖励信息
|
||||
rewardsBuilder := l.svcCtx.AgentRewardsModel.SelectBuilder().
|
||||
Where(squirrel.Eq{"agent_id": agentID}).
|
||||
Where(squirrel.Eq{"relation_agent_id": descendantIDs})
|
||||
rewardsList, err := l.svcCtx.AgentRewardsModel.FindAll(l.ctx, rewardsBuilder, "")
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级列表, 获取代理奖励信息%v", err)
|
||||
}
|
||||
rewardsMap := make(map[int64]float64)
|
||||
for _, v := range rewardsList {
|
||||
if v.RelationAgentId.Valid {
|
||||
rewardsMap[v.RelationAgentId.Int64] += v.Amount
|
||||
}
|
||||
}
|
||||
|
||||
// 并发查询佣金信息
|
||||
commissionBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().
|
||||
Where(squirrel.Eq{"agent_id": descendantIDs})
|
||||
commissionList, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, commissionBuilder, "")
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级列表, 获取代理佣金信息%v", err)
|
||||
}
|
||||
commissionMap := make(map[int64]float64)
|
||||
orderCountMap := make(map[int64]int64)
|
||||
for _, v := range commissionList {
|
||||
commissionMap[v.AgentId] += v.Amount
|
||||
orderCountMap[v.AgentId]++
|
||||
}
|
||||
|
||||
// 构建返回结果
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
descendantList = make([]types.AgentSubordinateList, 0, len(descendantIDs))
|
||||
for _, id := range descendantIDs {
|
||||
agent, exists := agentMap[id]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
mobile, err := crypto.DecryptMobile(agent.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息, 解密手机号失败: %v", err)
|
||||
}
|
||||
|
||||
subordinate := types.AgentSubordinateList{
|
||||
ID: id,
|
||||
Mobile: maskPhone(mobile),
|
||||
LevelName: agent.LevelName,
|
||||
CreateTime: createTimeMap[id].Format("2006-01-02 15:04:05"),
|
||||
TotalContribution: deductionMap[id] + rewardsMap[id],
|
||||
TotalEarnings: commissionMap[id],
|
||||
TotalOrders: orderCountMap[id],
|
||||
}
|
||||
descendantList = append(descendantList, subordinate)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.GetAgentSubordinateListResp{
|
||||
Total: total,
|
||||
List: descendantList,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 手机号脱敏
|
||||
func maskPhone(phone string) string {
|
||||
length := len(phone)
|
||||
if length < 8 {
|
||||
return phone // 如果长度太短,可能不是手机号,不处理
|
||||
}
|
||||
// 保留前3位和后4位
|
||||
return phone[:3] + strings.Repeat("*", length-7) + phone[length-4:]
|
||||
}
|
||||
66
app/main/api/internal/logic/agent/getagentwithdrawallogic.go
Normal file
66
app/main/api/internal/logic/agent/getagentwithdrawallogic.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentWithdrawalLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentWithdrawalLogic {
|
||||
return &GetAgentWithdrawalLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentWithdrawalLogic) GetAgentWithdrawal(req *types.GetWithdrawalReq) (resp *types.GetWithdrawalResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理提现列表, %v", err)
|
||||
}
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理提现列表, %v", err)
|
||||
}
|
||||
builder := l.svcCtx.AgentWithdrawalModel.SelectBuilder().Where(squirrel.Eq{
|
||||
"agent_id": agentModel.Id,
|
||||
})
|
||||
agentWithdrawalModelList, total, err := l.svcCtx.AgentWithdrawalModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理提现列表, 查找列表错误, %v", err)
|
||||
}
|
||||
|
||||
var list = make([]types.Withdrawal, 0)
|
||||
|
||||
if len(agentWithdrawalModelList) > 0 {
|
||||
for _, agentWithdrawalModel := range agentWithdrawalModelList {
|
||||
var withdrawal types.Withdrawal
|
||||
copyErr := copier.Copy(&withdrawal, agentWithdrawalModel)
|
||||
if copyErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理提现列表, %v", err)
|
||||
}
|
||||
withdrawal.CreateTime = agentWithdrawalModel.CreateTime.Format("2006-01-02 15:04:05")
|
||||
list = append(list, withdrawal)
|
||||
}
|
||||
}
|
||||
return &types.GetWithdrawalResp{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentWithdrawalTaxExemptionLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentWithdrawalTaxExemptionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentWithdrawalTaxExemptionLogic {
|
||||
return &GetAgentWithdrawalTaxExemptionLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentWithdrawalTaxExemptionLogic) GetAgentWithdrawalTaxExemption(req *types.GetWithdrawalTaxExemptionReq) (resp *types.GetWithdrawalTaxExemptionResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %+v", err)
|
||||
}
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理ID失败: %+v", err)
|
||||
}
|
||||
yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
|
||||
|
||||
agentWithdrawalTaxExemption, err := l.svcCtx.AgentWithdrawalTaxExemptionModel.FindOneByAgentIdYearMonth(l.ctx, agent.Id, yearMonth)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
agentWithdrawalTaxExemption, err = l.createMonthlyExemption(agent.Id, yearMonth)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建代理税务额度失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理税务额度失败: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &types.GetWithdrawalTaxExemptionResp{
|
||||
TotalExemptionAmount: agentWithdrawalTaxExemption.TotalExemptionAmount,
|
||||
UsedExemptionAmount: agentWithdrawalTaxExemption.UsedExemptionAmount,
|
||||
RemainingExemptionAmount: agentWithdrawalTaxExemption.RemainingExemptionAmount,
|
||||
TaxRate: l.svcCtx.Config.TaxConfig.TaxRate,
|
||||
}, nil
|
||||
}
|
||||
func (l *GetAgentWithdrawalTaxExemptionLogic) createMonthlyExemption(agentId int64, yearMonth int64) (*model.AgentWithdrawalTaxExemption, error) {
|
||||
exemption := &model.AgentWithdrawalTaxExemption{
|
||||
AgentId: agentId,
|
||||
YearMonth: yearMonth,
|
||||
TotalExemptionAmount: l.svcCtx.Config.TaxConfig.TaxExemptionAmount,
|
||||
UsedExemptionAmount: 0.00,
|
||||
RemainingExemptionAmount: l.svcCtx.Config.TaxConfig.TaxExemptionAmount,
|
||||
}
|
||||
|
||||
result, err := l.svcCtx.AgentWithdrawalTaxExemptionModel.Insert(l.ctx, nil, exemption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, _ := result.LastInsertId()
|
||||
exemption.Id = id
|
||||
return exemption, nil
|
||||
}
|
||||
46
app/main/api/internal/logic/agent/getlinkdatalogic.go
Normal file
46
app/main/api/internal/logic/agent/getlinkdatalogic.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetLinkDataLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetLinkDataLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLinkDataLogic {
|
||||
return &GetLinkDataLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetLinkDataLogic) GetLinkData(req *types.GetLinkDataReq) (resp *types.GetLinkDataResp, err error) {
|
||||
agentLinkModel, err := l.svcCtx.AgentLinkModel.FindOneByLinkIdentifier(l.ctx, req.LinkIdentifier)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理链接数据, %v", err)
|
||||
}
|
||||
|
||||
productModel, err := l.svcCtx.ProductModel.FindOne(l.ctx, agentLinkModel.ProductId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理链接数据, %v", err)
|
||||
}
|
||||
var product types.Product
|
||||
copier.Copy(&product, productModel)
|
||||
product.SellPrice = agentLinkModel.Price
|
||||
return &types.GetLinkDataResp{
|
||||
Product: product,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type SaveAgentMembershipUserConfigLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewSaveAgentMembershipUserConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SaveAgentMembershipUserConfigLogic {
|
||||
return &SaveAgentMembershipUserConfigLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *SaveAgentMembershipUserConfigLogic) SaveAgentMembershipUserConfig(req *types.SaveAgentMembershipUserConfigReq) error {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "保存会员代理报告配置,获取用户ID失败: %v", err)
|
||||
}
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "保存会员代理报告配置: %v", err)
|
||||
}
|
||||
|
||||
var agentMembershipUserConfigModel *model.AgentMembershipUserConfig
|
||||
agentMembershipUserConfigModel, err = l.svcCtx.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(l.ctx, agentModel.Id, req.ProductID)
|
||||
|
||||
// 检查记录是否存在
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
// 记录不存在,创建新的配置对象
|
||||
agentMembershipUserConfigModel = &model.AgentMembershipUserConfig{
|
||||
UserId: userID,
|
||||
AgentId: agentModel.Id,
|
||||
ProductId: req.ProductID,
|
||||
PriceRatio: req.PriceRatio,
|
||||
PriceIncreaseAmount: req.PriceIncreaseAmount,
|
||||
PriceRangeFrom: req.PriceRangeFrom,
|
||||
PriceRangeTo: req.PriceRangeTo,
|
||||
}
|
||||
|
||||
// 插入新记录
|
||||
_, err = l.svcCtx.AgentMembershipUserConfigModel.Insert(l.ctx, nil, agentMembershipUserConfigModel)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "保存会员代理报告配置,插入新记录失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 其他错误
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "保存会员代理报告配置,查询记录失败: %v", err)
|
||||
}
|
||||
|
||||
// 记录存在,更新现有配置
|
||||
agentMembershipUserConfigModel.PriceRatio = req.PriceRatio
|
||||
agentMembershipUserConfigModel.PriceIncreaseAmount = req.PriceIncreaseAmount
|
||||
agentMembershipUserConfigModel.PriceRangeFrom = req.PriceRangeFrom
|
||||
agentMembershipUserConfigModel.PriceRangeTo = req.PriceRangeTo
|
||||
|
||||
_, err = l.svcCtx.AgentMembershipUserConfigModel.Update(l.ctx, nil, agentMembershipUserConfigModel)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "保存会员代理报告配置,更新记录失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
31
app/main/api/internal/logic/app/getappversionlogic.go
Normal file
31
app/main/api/internal/logic/app/getappversionlogic.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAppVersionLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAppVersionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAppVersionLogic {
|
||||
return &GetAppVersionLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAppVersionLogic) GetAppVersion() (resp *types.GetAppVersionResp, err error) {
|
||||
return &types.GetAppVersionResp{
|
||||
Version: "1.0.0",
|
||||
WgtUrl: "https://www.quannengcha.com/app_version/qnc_1.0.0.wgt",
|
||||
}, nil
|
||||
}
|
||||
31
app/main/api/internal/logic/app/healthchecklogic.go
Normal file
31
app/main/api/internal/logic/app/healthchecklogic.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type HealthCheckLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewHealthCheckLogic(ctx context.Context, svcCtx *svc.ServiceContext) *HealthCheckLogic {
|
||||
return &HealthCheckLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *HealthCheckLogic) HealthCheck() (resp *types.HealthCheckResp, err error) {
|
||||
return &types.HealthCheckResp{
|
||||
Status: "UP",
|
||||
Message: "Service is healthy HahaHa",
|
||||
}, nil
|
||||
}
|
||||
105
app/main/api/internal/logic/auth/sendsmslogic.go
Normal file
105
app/main/api/internal/logic/auth/sendsmslogic.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
dysmsapi "github.com/alibabacloud-go/dysmsapi-20170525/v3/client"
|
||||
"github.com/alibabacloud-go/tea-utils/v2/service"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type SendSmsLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLogic {
|
||||
return &SendSmsLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 加密手机号失败: %v", err)
|
||||
}
|
||||
// 检查手机号是否在一分钟内已发送过验证码
|
||||
limitCodeKey := fmt.Sprintf("limit:%s:%s", req.ActionType, encryptedMobile)
|
||||
exists, err := l.svcCtx.Redis.Exists(limitCodeKey)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 读取redis缓存失败: %s", encryptedMobile)
|
||||
}
|
||||
|
||||
if exists {
|
||||
// 如果 Redis 中已经存在标记,说明在 1 分钟内请求过,返回错误
|
||||
return errors.Wrapf(xerr.NewErrMsg("一分钟内不能重复发送验证码"), "短信发送, 手机号1分钟内重复请求发送验证码: %s", encryptedMobile)
|
||||
}
|
||||
|
||||
code := fmt.Sprintf("%06d", rand.New(rand.NewSource(time.Now().UnixNano())).Intn(1000000))
|
||||
|
||||
// 发送短信
|
||||
smsResp, err := l.sendSmsRequest(req.Mobile, code)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 调用阿里客户端失败: %v", err)
|
||||
}
|
||||
if *smsResp.Body.Code != "OK" {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 阿里客户端响应失败: %s", *smsResp.Body.Message)
|
||||
}
|
||||
codeKey := fmt.Sprintf("%s:%s", req.ActionType, encryptedMobile)
|
||||
// 将验证码保存到 Redis,设置过期时间
|
||||
err = l.svcCtx.Redis.Setex(codeKey, code, l.svcCtx.Config.VerifyCode.ValidTime) // 验证码有效期5分钟
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 验证码设置过期时间失败: %v", err)
|
||||
}
|
||||
// 在 Redis 中设置 1 分钟的标记,限制重复请求
|
||||
err = l.svcCtx.Redis.Setex(limitCodeKey, code, 60) // 标记 1 分钟内不能重复请求
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 验证码设置限制重复请求失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateClient 创建阿里云短信客户端
|
||||
func (l *SendSmsLogic) CreateClient() (*dysmsapi.Client, error) {
|
||||
config := &openapi.Config{
|
||||
AccessKeyId: &l.svcCtx.Config.VerifyCode.AccessKeyID,
|
||||
AccessKeySecret: &l.svcCtx.Config.VerifyCode.AccessKeySecret,
|
||||
}
|
||||
config.Endpoint = tea.String(l.svcCtx.Config.VerifyCode.EndpointURL)
|
||||
return dysmsapi.NewClient(config)
|
||||
}
|
||||
|
||||
// sendSmsRequest 发送短信请求
|
||||
func (l *SendSmsLogic) sendSmsRequest(mobile, code string) (*dysmsapi.SendSmsResponse, error) {
|
||||
// 初始化阿里云短信客户端
|
||||
cli, err := l.CreateClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request := &dysmsapi.SendSmsRequest{
|
||||
SignName: tea.String(l.svcCtx.Config.VerifyCode.SignName),
|
||||
TemplateCode: tea.String(l.svcCtx.Config.VerifyCode.TemplateCode),
|
||||
PhoneNumbers: tea.String(mobile),
|
||||
TemplateParam: tea.String(fmt.Sprintf("{\"code\":\"%s\"}", code)),
|
||||
}
|
||||
runtime := &service.RuntimeOptions{}
|
||||
return cli.SendSmsWithOptions(request, runtime)
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetNotificationsLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetNotificationsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNotificationsLogic {
|
||||
return &GetNotificationsLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetNotificationsLogic) GetNotifications() (resp *types.GetNotificationsResp, err error) {
|
||||
// 获取今天的日期
|
||||
now := time.Now()
|
||||
|
||||
// 获取开始和结束日期的时间戳
|
||||
todayStart := now.Format("2006-01-02") + " 00:00:00"
|
||||
todayEnd := now.Format("2006-01-02") + " 23:59:59"
|
||||
|
||||
// 构建查询条件
|
||||
builder := l.svcCtx.GlobalNotificationsModel.SelectBuilder().
|
||||
Where("status = ?", "active").
|
||||
Where("(start_date IS NULL OR start_date <= ?)", todayEnd). // start_date 是 NULL 或者小于等于今天结束时间
|
||||
Where("(end_date IS NULL OR end_date >= ?)", todayStart) // end_date 是 NULL 或者大于等于今天开始时间
|
||||
|
||||
notificationsModelList, findErr := l.svcCtx.GlobalNotificationsModel.FindAll(l.ctx, builder, "")
|
||||
if findErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "全局通知, 查找通知失败, err:%+v", findErr)
|
||||
}
|
||||
|
||||
var notifications []types.Notification
|
||||
copyErr := copier.Copy(¬ifications, ¬ificationsModelList)
|
||||
if copyErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "全局通知, 复制结构体失败, err:%+v", copyErr)
|
||||
}
|
||||
|
||||
return &types.GetNotificationsResp{Notifications: notifications}, nil
|
||||
}
|
||||
216
app/main/api/internal/logic/pay/alipaycallbacklogic.go
Normal file
216
app/main/api/internal/logic/pay/alipaycallbacklogic.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
"znc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/smartwalle/alipay/v3"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/model"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type AlipayCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAlipayCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AlipayCallbackLogic {
|
||||
return &AlipayCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AlipayCallbackLogic) AlipayCallback(w http.ResponseWriter, r *http.Request) error {
|
||||
notification, err := l.svcCtx.AlipayService.HandleAliPaymentNotification(r)
|
||||
if err != nil {
|
||||
logx.Errorf("支付宝支付回调,%v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 根据订单号前缀判断订单类型
|
||||
orderNo := notification.OutTradeNo
|
||||
if strings.HasPrefix(orderNo, "Q_") {
|
||||
// 查询订单处理
|
||||
return l.handleQueryOrderPayment(w, notification)
|
||||
} else if strings.HasPrefix(orderNo, "A_") {
|
||||
// 代理会员订单处理
|
||||
return l.handleAgentVipOrderPayment(w, notification)
|
||||
} else {
|
||||
// 兼容旧订单,假设没有前缀的是查询订单
|
||||
return l.handleQueryOrderPayment(w, notification)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理查询订单支付
|
||||
func (l *AlipayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter, notification *alipay.Notification) error {
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, notification.OutTradeNo)
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("支付宝支付回调,查找订单失败: %+v", findOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
if order.Status != "pending" {
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, order.UserId)
|
||||
if err != nil {
|
||||
logx.Errorf("支付宝支付回调,查找用户失败: %+v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
amount := lzUtils.ToAlipayAmount(order.Amount)
|
||||
if user.Inside != 1 {
|
||||
// 确保订单金额和状态正确,防止重复更新
|
||||
if amount != notification.TotalAmount {
|
||||
logx.Errorf("支付宝支付回调,金额不一致")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
switch notification.TradeStatus {
|
||||
case alipay.TradeStatusSuccess:
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(notification.TradeNo)
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
logx.Errorf("支付宝支付回调,修改订单信息失败: %+v", updateErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
if order.Status == "paid" {
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
logx.Errorf("异步任务调度失败: %v", asyncErr)
|
||||
return asyncErr
|
||||
}
|
||||
}
|
||||
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理代理会员订单支付
|
||||
func (l *AlipayCallbackLogic) handleAgentVipOrderPayment(w http.ResponseWriter, notification *alipay.Notification) error {
|
||||
agentOrder, findAgentOrderErr := l.svcCtx.AgentMembershipRechargeOrderModel.FindOneByOrderNo(l.ctx, notification.OutTradeNo)
|
||||
if findAgentOrderErr != nil {
|
||||
logx.Errorf("支付宝支付回调,查找代理会员订单失败: %+v", findAgentOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
if agentOrder.Status != "pending" {
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, agentOrder.UserId)
|
||||
if err != nil {
|
||||
logx.Errorf("支付宝支付回调,查找用户失败: %+v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
amount := lzUtils.ToAlipayAmount(agentOrder.Amount)
|
||||
if user.Inside != 1 {
|
||||
// 确保订单金额和状态正确,防止重复更新
|
||||
if amount != notification.TotalAmount {
|
||||
logx.Errorf("支付宝支付回调,金额不一致")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
switch notification.TradeStatus {
|
||||
case alipay.TradeStatusSuccess:
|
||||
agentOrder.Status = "paid"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if agentOrder.Status == "paid" {
|
||||
err = l.svcCtx.AgentModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOne(transCtx, agentOrder.AgentId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("查找代理信息失败: %+v", err)
|
||||
}
|
||||
agentOrder.PlatformOrderId = lzUtils.StringToNullString(notification.TradeNo)
|
||||
if updateErr := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(l.ctx, nil, agentOrder); updateErr != nil {
|
||||
return fmt.Errorf("修改代理会员订单信息失败: %+v", updateErr)
|
||||
}
|
||||
|
||||
// 设置会员等级
|
||||
agentModel.LevelName = agentOrder.LevelName
|
||||
|
||||
// 延长会员时间
|
||||
// 检查是否是同级续费并记录到日志
|
||||
isRenewal := agentModel.LevelName == agentOrder.LevelName && agentModel.MembershipExpiryTime.Valid
|
||||
if isRenewal {
|
||||
logx.Infof("代理会员续费成功,会员ID:%d,等级:%s", agentModel.Id, agentModel.LevelName)
|
||||
} else {
|
||||
logx.Infof("代理会员新购或升级成功,会员ID:%d,等级:%s", agentModel.Id, agentModel.LevelName)
|
||||
}
|
||||
agentModel.MembershipExpiryTime = lzUtils.RenewMembership(agentModel.MembershipExpiryTime)
|
||||
|
||||
if updateErr := l.svcCtx.AgentModel.UpdateWithVersion(l.ctx, nil, agentModel); updateErr != nil {
|
||||
return fmt.Errorf("修改代理信息失败: %+v", updateErr)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logx.Errorf("支付宝支付回调,处理代理会员订单失败: %+v", err)
|
||||
refundErr := l.handleRefund(agentOrder)
|
||||
if refundErr != nil {
|
||||
logx.Errorf("支付宝支付回调,退款失败: %+v", refundErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *AlipayCallbackLogic) handleRefund(order *model.AgentMembershipRechargeOrder) error {
|
||||
ctx := context.Background()
|
||||
// 退款
|
||||
if order.PaymentMethod == "wechat" {
|
||||
refundErr := l.svcCtx.WechatPayService.WeChatRefund(ctx, order.OrderNo, order.Amount, order.Amount)
|
||||
if refundErr != nil {
|
||||
return refundErr
|
||||
}
|
||||
} else {
|
||||
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
|
||||
if refundErr != nil {
|
||||
return refundErr
|
||||
}
|
||||
if refund.IsSuccess() {
|
||||
logx.Errorf("支付宝退款成功, orderID: %d", order.Id)
|
||||
// 更新订单状态为退款
|
||||
order.Status = "refunded"
|
||||
updateOrderErr := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(ctx, nil, order)
|
||||
if updateOrderErr != nil {
|
||||
logx.Errorf("更新订单状态失败,订单ID: %d, 错误: %v", order.Id, updateOrderErr)
|
||||
return fmt.Errorf("更新订单状态失败: %v", updateOrderErr)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
logx.Errorf("支付宝退款失败:%v", refundErr)
|
||||
return refundErr
|
||||
}
|
||||
// 直接成功
|
||||
}
|
||||
return nil
|
||||
}
|
||||
82
app/main/api/internal/logic/pay/iapcallbacklogic.go
Normal file
82
app/main/api/internal/logic/pay/iapcallbacklogic.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type IapCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewIapCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *IapCallbackLogic {
|
||||
return &IapCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *IapCallbackLogic) IapCallback(req *types.IapCallbackReq) error {
|
||||
// Step 1: 查找订单
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOne(l.ctx, req.OrderID)
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("苹果内购支付回调,查找订单失败: %+v", findOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Step 2: 验证订单状态
|
||||
if order.Status != "pending" {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 订单状态异常: %+v", order)
|
||||
}
|
||||
|
||||
// Step 3: 调用 VerifyReceipt 验证苹果支付凭证
|
||||
//receipt := req.TransactionReceipt // 从请求中获取支付凭证
|
||||
//verifyResponse, verifyErr := l.svcCtx.ApplePayService.VerifyReceipt(l.ctx, receipt)
|
||||
//if verifyErr != nil {
|
||||
// return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 验证订单异常: %+v", verifyErr)
|
||||
//}
|
||||
|
||||
// Step 4: 验证订单
|
||||
//product, findProductErr := l.svcCtx.ProductModel.FindOne(l.ctx, order.Id)
|
||||
//if findProductErr != nil {
|
||||
// return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "苹果内购支付回调, 获取订单相关商品失败: %+v", findProductErr)
|
||||
//}
|
||||
//isProductMatched := false
|
||||
//appleProductID := l.svcCtx.ApplePayService.GetIappayAppID(product.ProductEn)
|
||||
//for _, item := range verifyResponse.Receipt.InApp {
|
||||
// if item.ProductID == appleProductID {
|
||||
// isProductMatched = true
|
||||
// order.PlatformOrderId = lzUtils.StringToNullString(item.TransactionID) // 记录交易 ID
|
||||
// break
|
||||
// }
|
||||
//}
|
||||
//if !isProductMatched {
|
||||
// return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 商品 ID 不匹配,订单 ID: %d, 回调苹果商品 ID: %s", order.Id, verifyResponse.Receipt.InApp[0].ProductID)
|
||||
//}
|
||||
|
||||
// Step 5: 更新订单状态 mm
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
|
||||
// 更新订单到数据库
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 修改订单信息失败: %+v", updateErr)
|
||||
}
|
||||
|
||||
// Step 6: 处理订单完成后的逻辑
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调,异步任务调度失败: %v", asyncErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
49
app/main/api/internal/logic/pay/paymentchecklogic.go
Normal file
49
app/main/api/internal/logic/pay/paymentchecklogic.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type PaymentCheckLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewPaymentCheckLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentCheckLogic {
|
||||
return &PaymentCheckLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *PaymentCheckLogic) PaymentCheck(req *types.PaymentCheckReq) (resp *types.PaymentCheckResp, err error) {
|
||||
if strings.HasPrefix(req.OrderNo, "A_") {
|
||||
order, err := l.svcCtx.AgentMembershipRechargeOrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询订单失败: %v", err)
|
||||
}
|
||||
return &types.PaymentCheckResp{
|
||||
Type: "agent_vip",
|
||||
Status: order.Status,
|
||||
}, nil
|
||||
} else {
|
||||
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询订单失败: %v", err)
|
||||
}
|
||||
return &types.PaymentCheckResp{
|
||||
Type: "query",
|
||||
Status: order.Status,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
227
app/main/api/internal/logic/pay/paymentlogic.go
Normal file
227
app/main/api/internal/logic/pay/paymentlogic.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
"znc-server/app/main/api/internal/types"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/common/ctxdata"
|
||||
"znc-server/common/xerr"
|
||||
"znc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type PaymentLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
type PaymentTypeResp struct {
|
||||
amount float64
|
||||
outTradeNo string
|
||||
description string
|
||||
}
|
||||
|
||||
func NewPaymentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentLogic {
|
||||
return &PaymentLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp, err error) {
|
||||
var paymentTypeResp *PaymentTypeResp
|
||||
var prepayData interface{}
|
||||
l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
switch req.PayType {
|
||||
case "agent_vip":
|
||||
paymentTypeResp, err = l.AgentVipOrderPayment(req, session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "query":
|
||||
paymentTypeResp, err = l.QueryOrderPayment(req, session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var createOrderErr error
|
||||
if req.PayMethod == "wechat" {
|
||||
prepayData, createOrderErr = l.svcCtx.WechatPayService.CreateWechatOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo)
|
||||
} else if req.PayMethod == "alipay" {
|
||||
prepayData, createOrderErr = l.svcCtx.AlipayService.CreateAlipayOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo)
|
||||
} else if req.PayMethod == "appleiap" {
|
||||
prepayData = l.svcCtx.ApplePayService.GetIappayAppID(paymentTypeResp.outTradeNo)
|
||||
}
|
||||
if createOrderErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 创建支付订单失败: %+v", createOrderErr)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch v := prepayData.(type) {
|
||||
case string:
|
||||
// 如果 prepayData 是字符串类型,直接返回
|
||||
return &types.PaymentResp{PrepayId: v, OrderNo: paymentTypeResp.outTradeNo}, nil
|
||||
default:
|
||||
return &types.PaymentResp{PrepayData: prepayData, OrderNo: paymentTypeResp.outTradeNo}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) {
|
||||
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取用户信息失败, %+v", getUidErr)
|
||||
}
|
||||
outTradeNo := req.Id
|
||||
redisKey := fmt.Sprintf(types.QueryCacheKey, userID, outTradeNo)
|
||||
cache, cacheErr := l.svcCtx.Redis.GetCtx(l.ctx, redisKey)
|
||||
if cacheErr != nil {
|
||||
if cacheErr == redis.Nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("订单已过期"), "生成订单, 缓存不存在, %+v", cacheErr)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取缓存失败, %+v", cacheErr)
|
||||
}
|
||||
var data types.QueryCacheLoad
|
||||
err = json.Unmarshal([]byte(cache), &data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 解析缓存内容失败, %v", err)
|
||||
}
|
||||
|
||||
product, err := l.svcCtx.ProductModel.FindOneByProductEn(l.ctx, data.Product)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 查找产品错误: %v", err)
|
||||
}
|
||||
|
||||
var amount float64
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 获取用户信息失败: %v", err)
|
||||
}
|
||||
|
||||
if data.AgentIdentifier != "" {
|
||||
agentLinkModel, findAgentLinkErr := l.svcCtx.AgentLinkModel.FindOneByLinkIdentifier(l.ctx, data.AgentIdentifier)
|
||||
if findAgentLinkErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 获取代理订单失败: %+v", findAgentLinkErr)
|
||||
}
|
||||
amount = agentLinkModel.Price
|
||||
} else {
|
||||
amount = product.SellPrice
|
||||
}
|
||||
|
||||
if user.Inside == 1 {
|
||||
amount = 0.01
|
||||
}
|
||||
var orderID int64
|
||||
order := model.Order{
|
||||
OrderNo: outTradeNo,
|
||||
UserId: userID,
|
||||
ProductId: product.Id,
|
||||
PaymentPlatform: req.PayMethod,
|
||||
PaymentScene: "app",
|
||||
Amount: amount,
|
||||
Status: "pending",
|
||||
}
|
||||
orderInsertResult, insertOrderErr := l.svcCtx.OrderModel.Insert(l.ctx, session, &order)
|
||||
if insertOrderErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存订单失败: %+v", insertOrderErr)
|
||||
}
|
||||
insertedOrderID, lastInsertIdErr := orderInsertResult.LastInsertId()
|
||||
if lastInsertIdErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 获取保存订单ID失败: %+v", lastInsertIdErr)
|
||||
}
|
||||
orderID = insertedOrderID
|
||||
|
||||
if data.AgentIdentifier != "" {
|
||||
agent, parsingErr := l.agentParsing(data.AgentIdentifier)
|
||||
if parsingErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 解析代理标识符失败: %+v", parsingErr)
|
||||
}
|
||||
var agentOrder model.AgentOrder
|
||||
agentOrder.OrderId = orderID
|
||||
agentOrder.AgentId = agent.AgentID
|
||||
_, agentOrderInsert := l.svcCtx.AgentOrderModel.Insert(l.ctx, session, &agentOrder)
|
||||
if agentOrderInsert != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存代理订单失败: %+v", agentOrderInsert)
|
||||
}
|
||||
}
|
||||
return &PaymentTypeResp{amount: amount, outTradeNo: outTradeNo, description: product.ProductName}, nil
|
||||
}
|
||||
func (l *PaymentLogic) AgentVipOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) {
|
||||
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取用户信息失败, %+v", getUidErr)
|
||||
}
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 获取用户信息失败: %v", err)
|
||||
}
|
||||
// 查询用户代理信息
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err)
|
||||
}
|
||||
redisKey := fmt.Sprintf(types.AgentVipCacheKey, userID, req.Id)
|
||||
cache, cacheErr := l.svcCtx.Redis.GetCtx(l.ctx, redisKey)
|
||||
if cacheErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取缓存失败, %+v", cacheErr)
|
||||
}
|
||||
var agentVipCache types.AgentVipCache
|
||||
err = json.Unmarshal([]byte(cache), &agentVipCache)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 解析缓存内容失败, %+v", err)
|
||||
}
|
||||
agentMembershipConfig, err := l.svcCtx.AgentMembershipConfigModel.FindOneByLevelName(l.ctx, agentVipCache.Type)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取代理会员配置失败, %+v", err)
|
||||
}
|
||||
|
||||
amount := agentMembershipConfig.Price.Float64
|
||||
if user.Inside == 1 {
|
||||
amount = 0.01
|
||||
}
|
||||
agentMembershipRechargeOrder := model.AgentMembershipRechargeOrder{
|
||||
OrderNo: req.Id,
|
||||
UserId: userID,
|
||||
AgentId: agentModel.Id,
|
||||
Amount: amount,
|
||||
PaymentMethod: req.PayMethod,
|
||||
LevelName: agentVipCache.Type,
|
||||
Status: "pending",
|
||||
}
|
||||
_, err = l.svcCtx.AgentMembershipRechargeOrderModel.Insert(l.ctx, session, &agentMembershipRechargeOrder)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存代理会员充值订单失败: %+v", err)
|
||||
}
|
||||
return &PaymentTypeResp{amount: amount, outTradeNo: req.Id, description: fmt.Sprintf("%s代理会员充值", agentMembershipConfig.LevelName)}, nil
|
||||
}
|
||||
func (l *PaymentLogic) agentParsing(agentIdentifier string) (*types.AgentIdentifier, error) {
|
||||
key, decodeErr := hex.DecodeString("8e3e7a2f60edb49221e953b9c029ed10")
|
||||
if decodeErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 获取AES密钥失败: %+v", decodeErr)
|
||||
}
|
||||
// Encrypt the params
|
||||
|
||||
encrypted, err := crypto.AesDecryptURL(agentIdentifier, key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, %v", err)
|
||||
}
|
||||
var agentIdentifierStruct types.AgentIdentifier
|
||||
err = json.Unmarshal(encrypted, &agentIdentifierStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务,反序列化失败 %v", err)
|
||||
}
|
||||
return &agentIdentifierStruct, nil
|
||||
}
|
||||
215
app/main/api/internal/logic/pay/wechatpaycallbacklogic.go
Normal file
215
app/main/api/internal/logic/pay/wechatpaycallbacklogic.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
"znc-server/app/main/api/internal/service"
|
||||
"znc-server/app/main/model"
|
||||
"znc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type WechatPayCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewWechatPayCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WechatPayCallbackLogic {
|
||||
return &WechatPayCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *WechatPayCallbackLogic) WechatPayCallback(w http.ResponseWriter, r *http.Request) error {
|
||||
notification, err := l.svcCtx.WechatPayService.HandleWechatPayNotification(l.ctx, r)
|
||||
if err != nil {
|
||||
logx.Errorf("微信支付回调,%v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 根据订单号前缀判断订单类型
|
||||
orderNo := *notification.OutTradeNo
|
||||
if strings.HasPrefix(orderNo, "Q_") {
|
||||
// 查询订单处理
|
||||
return l.handleQueryOrderPayment(w, notification)
|
||||
} else if strings.HasPrefix(orderNo, "A_") {
|
||||
// 代理会员订单处理
|
||||
return l.handleAgentVipOrderPayment(w, notification)
|
||||
} else {
|
||||
// 兼容旧订单,假设没有前缀的是查询订单
|
||||
return l.handleQueryOrderPayment(w, notification)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理查询订单支付
|
||||
func (l *WechatPayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter, notification *payments.Transaction) error {
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, *notification.OutTradeNo)
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("微信支付回调,查找订单信息失败: %+v", findOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
amount := lzUtils.ToWechatAmount(order.Amount)
|
||||
if amount != *notification.Amount.Total {
|
||||
logx.Errorf("微信支付回调,金额不一致")
|
||||
return nil
|
||||
}
|
||||
|
||||
if order.Status != "pending" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
switch *notification.TradeState {
|
||||
case service.TradeStateSuccess:
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
case service.TradeStateClosed:
|
||||
order.Status = "closed"
|
||||
order.CloseTime = lzUtils.TimeToNullTime(time.Now())
|
||||
case service.TradeStateRevoked:
|
||||
order.Status = "failed"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(*notification.TransactionId)
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
logx.Errorf("微信支付回调,更新订单失败%+v", updateErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
if order.Status == "paid" {
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
logx.Errorf("异步任务调度失败: %v", asyncErr)
|
||||
return asyncErr
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理代理会员订单支付
|
||||
func (l *WechatPayCallbackLogic) handleAgentVipOrderPayment(w http.ResponseWriter, notification *payments.Transaction) error {
|
||||
agentOrder, findAgentOrderErr := l.svcCtx.AgentMembershipRechargeOrderModel.FindOneByOrderNo(l.ctx, *notification.OutTradeNo)
|
||||
if findAgentOrderErr != nil {
|
||||
logx.Errorf("微信支付回调,查找代理会员订单失败: %+v", findAgentOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
if agentOrder.Status != "pending" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, agentOrder.UserId)
|
||||
if err != nil {
|
||||
logx.Errorf("微信支付回调,查找用户失败: %+v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
amount := lzUtils.ToWechatAmount(agentOrder.Amount)
|
||||
if user.Inside != 1 {
|
||||
if amount != *notification.Amount.Total {
|
||||
logx.Errorf("微信支付回调,金额不一致")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
switch *notification.TradeState {
|
||||
case service.TradeStateSuccess:
|
||||
agentOrder.Status = "paid"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if agentOrder.Status == "paid" {
|
||||
err = l.svcCtx.AgentModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOne(transCtx, agentOrder.AgentId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("查找代理信息失败: %+v", err)
|
||||
}
|
||||
|
||||
agentOrder.PlatformOrderId = lzUtils.StringToNullString(*notification.TransactionId)
|
||||
if updateErr := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(l.ctx, nil, agentOrder); updateErr != nil {
|
||||
return fmt.Errorf("修改代理会员订单信息失败: %+v", updateErr)
|
||||
}
|
||||
|
||||
// 设置会员等级
|
||||
agentModel.LevelName = agentOrder.LevelName
|
||||
|
||||
// 延长会员时间
|
||||
isRenewal := agentModel.LevelName == agentOrder.LevelName && agentModel.MembershipExpiryTime.Valid
|
||||
if isRenewal {
|
||||
logx.Infof("代理会员续费成功,会员ID:%d,等级:%s", agentModel.Id, agentModel.LevelName)
|
||||
} else {
|
||||
logx.Infof("代理会员新购或升级成功,会员ID:%d,等级:%s", agentModel.Id, agentModel.LevelName)
|
||||
}
|
||||
agentModel.MembershipExpiryTime = lzUtils.RenewMembership(agentModel.MembershipExpiryTime)
|
||||
|
||||
if updateErr := l.svcCtx.AgentModel.UpdateWithVersion(l.ctx, nil, agentModel); updateErr != nil {
|
||||
return fmt.Errorf("修改代理信息失败: %+v", updateErr)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logx.Errorf("微信支付回调,处理代理会员订单失败: %+v", err)
|
||||
refundErr := l.handleRefund(agentOrder)
|
||||
if refundErr != nil {
|
||||
logx.Errorf("微信支付回调,退款失败: %+v", refundErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *WechatPayCallbackLogic) handleRefund(order *model.AgentMembershipRechargeOrder) error {
|
||||
ctx := context.Background()
|
||||
// 退款
|
||||
if order.PaymentMethod == "wechat" {
|
||||
refundErr := l.svcCtx.WechatPayService.WeChatRefund(ctx, order.OrderNo, order.Amount, order.Amount)
|
||||
if refundErr != nil {
|
||||
return refundErr
|
||||
}
|
||||
} else {
|
||||
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
|
||||
if refundErr != nil {
|
||||
return refundErr
|
||||
}
|
||||
if refund.IsSuccess() {
|
||||
logx.Errorf("支付宝退款成功, orderID: %d", order.Id)
|
||||
// 更新订单状态为退款
|
||||
order.Status = "refunded"
|
||||
updateOrderErr := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(ctx, nil, order)
|
||||
if updateOrderErr != nil {
|
||||
logx.Errorf("更新订单状态失败,订单ID: %d, 错误: %v", order.Id, updateOrderErr)
|
||||
return fmt.Errorf("更新订单状态失败: %v", updateOrderErr)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
logx.Errorf("支付宝退款失败:%v", refundErr)
|
||||
return refundErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"znc-server/app/main/api/internal/svc"
|
||||
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type WechatPayRefundCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewWechatPayRefundCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WechatPayRefundCallbackLogic {
|
||||
return &WechatPayRefundCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *WechatPayRefundCallbackLogic) WechatPayRefundCallback(w http.ResponseWriter, r *http.Request) error {
|
||||
notification, err := l.svcCtx.WechatPayService.HandleRefundNotification(l.ctx, r)
|
||||
if err != nil {
|
||||
logx.Errorf("微信退款回调,%v", err)
|
||||
return nil
|
||||
}
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, *notification.OutTradeNo)
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("微信退款回调,查找订单信息失败: %+v", findOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
switch *notification.Status {
|
||||
case refunddomestic.STATUS_SUCCESS:
|
||||
order.Status = "refunded"
|
||||
case refunddomestic.STATUS_ABNORMAL:
|
||||
// 异常
|
||||
return nil
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
logx.Errorf("微信退款回调,更新订单失败%+v", updateErr)
|
||||
return nil
|
||||
}
|
||||
// 响应微信回调成功
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success")) // 确保只写入一次响应
|
||||
return nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user