first commit

This commit is contained in:
2025-11-27 13:09:54 +08:00
commit 3440744179
570 changed files with 61861 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,33 @@
package admin_agent
import (
"context"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminAuditRealNameLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminAuditRealNameLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminAuditRealNameLogic {
return &AdminAuditRealNameLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
// AdminAuditRealName 实名认证审核(已废弃:实名认证改为三要素核验,无需审核)
func (l *AdminAuditRealNameLogic) AdminAuditRealName(req *types.AdminAuditRealNameReq) (resp *types.AdminAuditRealNameResp, err error) {
// 该接口已废弃,实名认证现在通过三要素核验自动完成,无需人工审核
return nil, errors.Wrapf(xerr.NewErrMsg("该接口已废弃,实名认证改为三要素核验,无需审核"), "")
}

View File

@@ -0,0 +1,175 @@
package admin_agent
import (
"context"
"database/sql"
"fmt"
"time"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/lzUtils"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminAuditWithdrawalLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminAuditWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminAuditWithdrawalLogic {
return &AdminAuditWithdrawalLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminAuditWithdrawalLogic) AdminAuditWithdrawal(req *types.AdminAuditWithdrawalReq) (resp *types.AdminAuditWithdrawalResp, err error) {
// 1. 查询提现记录
withdrawal, err := l.svcCtx.AgentWithdrawalModel.FindOne(l.ctx, req.WithdrawalId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询提现记录失败, %v", err)
}
// 3. 检查状态(必须是待审核状态)
if withdrawal.Status != 1 {
return nil, errors.Wrapf(xerr.NewErrMsg("该提现记录已处理"), "")
}
// 4. 使用事务处理审核
err = l.svcCtx.AgentWithdrawalModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
if req.Status == 2 { // 审核通过
// 4.1 更新提现记录状态为提现中
withdrawal.Status = 4 // 提现中
withdrawal.Remark = sql.NullString{String: req.Remark, Valid: true}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
// 4.2 调用支付宝转账接口
outBizNo := withdrawal.WithdrawNo
transferResp, err := l.svcCtx.AlipayService.AliTransfer(transCtx, withdrawal.PayeeAccount, withdrawal.PayeeName, withdrawal.ActualAmount, "代理提现", outBizNo)
if err != nil {
// 转账失败,更新状态为失败
withdrawal.Status = 6 // 提现失败
withdrawal.Remark = sql.NullString{String: fmt.Sprintf("转账失败: %v", err), Valid: true}
l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal)
// 解冻余额
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
if err == nil {
wallet.FrozenBalance -= withdrawal.Amount
wallet.Balance += withdrawal.Amount
l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet)
}
return errors.Wrapf(err, "支付宝转账失败")
}
// 4.3 根据转账结果更新状态
switch transferResp.Status {
case "SUCCESS":
// 转账成功
withdrawal.Status = 5 // 提现成功
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
// 更新钱包(解冻并扣除)
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
if err != nil {
return errors.Wrapf(err, "查询钱包失败")
}
wallet.FrozenBalance -= withdrawal.Amount
wallet.WithdrawnAmount += withdrawal.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
return errors.Wrapf(err, "更新钱包失败")
}
// 更新扣税记录状态
taxBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("withdrawal_id = ? AND del_state = ?", withdrawal.Id, globalkey.DelStateNo)
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(transCtx, taxBuilder, "")
if err == nil && len(taxRecords) > 0 {
taxRecord := taxRecords[0]
taxRecord.TaxStatus = 2 // 已扣税
taxRecord.TaxTime = lzUtils.TimeToNullTime(time.Now())
l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(transCtx, session, taxRecord)
}
case "FAIL":
// 转账失败
withdrawal.Status = 6 // 提现失败
errorMsg := l.mapAlipayError(transferResp.SubCode)
withdrawal.Remark = sql.NullString{String: errorMsg, Valid: true}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
// 解冻余额
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
if err == nil {
wallet.FrozenBalance -= withdrawal.Amount
wallet.Balance += withdrawal.Amount
l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet)
}
case "DEALING":
// 处理中,保持提现中状态,后续通过轮询更新
// 状态已经是4提现中无需更新
}
} else if req.Status == 3 { // 审核拒绝
// 4.1 更新提现记录状态为拒绝
withdrawal.Status = 3 // 审核拒绝
withdrawal.Remark = sql.NullString{String: req.Remark, Valid: true}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
// 4.2 解冻余额
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
if err != nil {
return errors.Wrapf(err, "查询钱包失败")
}
wallet.FrozenBalance -= withdrawal.Amount
wallet.Balance += withdrawal.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
return errors.Wrapf(err, "更新钱包失败")
}
}
return nil
})
if err != nil {
return nil, err
}
return &types.AdminAuditWithdrawalResp{
Success: true,
}, nil
}
// mapAlipayError 映射支付宝错误码
func (l *AdminAuditWithdrawalLogic) mapAlipayError(code string) string {
errorMapping := map[string]string{
"PAYEE_USERINFO_ERROR": "收款方姓名或信息不匹配",
"PAYEE_CARD_INFO_ERROR": "收款支付宝账号及户名不一致",
"PAYEE_IDENTITY_NOT_MATCH": "收款方身份信息不匹配",
"PAYEE_USER_IS_INST": "收款方为金融机构,不能使用提现功能",
"PAYEE_USER_TYPE_ERROR": "该支付宝账号类型不支持提现",
}
if msg, ok := errorMapping[code]; ok {
return msg
}
return "系统错误,请联系客服"
}

View File

@@ -0,0 +1,100 @@
package admin_agent
import (
"context"
"database/sql"
"time"
"ycc-server/app/main/model"
"ycc-server/common/tool"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGenerateDiamondInviteCodeLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGenerateDiamondInviteCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGenerateDiamondInviteCodeLogic {
return &AdminGenerateDiamondInviteCodeLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGenerateDiamondInviteCodeLogic) AdminGenerateDiamondInviteCode(req *types.AdminGenerateDiamondInviteCodeReq) (resp *types.AdminGenerateDiamondInviteCodeResp, err error) {
// 1. 验证生成数量
if req.Count <= 0 || req.Count > 100 {
return nil, errors.Wrapf(xerr.NewErrMsg("生成数量必须在1-100之间"), "")
}
// 2. 生成钻石邀请码平台发放agent_id为NULLtarget_level为3
codes := make([]string, 0, req.Count)
var expireTime sql.NullTime
if req.ExpireDays > 0 {
expireTime = sql.NullTime{
Time: time.Now().AddDate(0, 0, int(req.ExpireDays)),
Valid: true,
}
}
err = l.svcCtx.AgentInviteCodeModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
for i := int64(0); i < req.Count; i++ {
// 生成8位随机邀请码大小写字母+数字)
var code string
maxRetries := 10 // 最大重试次数
for retry := 0; retry < maxRetries; retry++ {
code = tool.Krand(8, tool.KC_RAND_KIND_ALL)
// 检查邀请码是否已存在
_, err := l.svcCtx.AgentInviteCodeModel.FindOneByCode(transCtx, code)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 邀请码不存在,可以使用
break
}
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "检查邀请码失败, %v", err)
}
// 邀请码已存在,继续生成
if retry == maxRetries-1 {
return errors.Wrapf(xerr.NewErrMsg("生成邀请码失败,请重试"), "")
}
}
// 创建邀请码记录(平台发放的钻石邀请码)
inviteCode := &model.AgentInviteCode{
Code: code,
AgentId: sql.NullInt64{Valid: false}, // NULL表示平台发放
TargetLevel: 3, // 钻石代理
Status: 0, // 未使用
ExpireTime: expireTime,
Remark: sql.NullString{String: req.Remark, Valid: req.Remark != ""},
}
_, err := l.svcCtx.AgentInviteCodeModel.Insert(transCtx, session, inviteCode)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建邀请码失败, %v", err)
}
codes = append(codes, code)
}
return nil
})
if err != nil {
return nil, err
}
return &types.AdminGenerateDiamondInviteCodeResp{
Codes: codes,
}, nil
}

View File

@@ -0,0 +1,74 @@
package admin_agent
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-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筛选
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
}

View File

@@ -0,0 +1,73 @@
package admin_agent
import (
"context"
"strconv"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentConfigLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentConfigLogic {
return &AdminGetAgentConfigLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentConfigLogic) AdminGetAgentConfig() (resp *types.AdminGetAgentConfigResp, err error) {
// 获取配置值的辅助函数
getConfigFloat := func(key string) float64 {
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, key)
if err != nil {
return 0
}
value, _ := strconv.ParseFloat(config.ConfigValue, 64)
return value
}
// 获取等级加成配置
level1Bonus := getConfigFloat("level_1_bonus")
level2Bonus := getConfigFloat("level_2_bonus")
level3Bonus := getConfigFloat("level_3_bonus")
// 获取升级费用配置
upgradeToGoldFee := getConfigFloat("upgrade_to_gold_fee")
upgradeToDiamondFee := getConfigFloat("upgrade_to_diamond_fee")
// 获取升级返佣配置
upgradeToGoldRebate := getConfigFloat("upgrade_to_gold_rebate")
upgradeToDiamondRebate := getConfigFloat("upgrade_to_diamond_rebate")
return &types.AdminGetAgentConfigResp{
BasePrice: getConfigFloat("base_price"),
SystemMaxPrice: getConfigFloat("system_max_price"),
PriceThreshold: getConfigFloat("price_threshold"),
PriceFeeRate: getConfigFloat("price_fee_rate"),
LevelBonus: types.LevelBonusConfig{
Normal: int64(level1Bonus),
Gold: int64(level2Bonus),
Diamond: int64(level3Bonus),
},
UpgradeFee: types.UpgradeFeeConfig{
NormalToGold: upgradeToGoldFee,
NormalToDiamond: upgradeToDiamondFee,
GoldToDiamond: upgradeToDiamondFee - upgradeToGoldFee,
},
UpgradeRebate: types.UpgradeRebateConfig{
NormalToGoldRebate: upgradeToGoldRebate,
ToDiamondRebate: upgradeToDiamondRebate,
},
TaxRate: getConfigFloat("tax_rate"),
TaxExemptionAmount: getConfigFloat("tax_exemption_amount"),
}, nil
}

View File

@@ -0,0 +1,82 @@
package admin_agent
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-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)
}
// 如果传入ProductId添加筛选条件
if req.ProductId != nil {
builder = builder.Where("product_id = ?", *req.ProductId)
}
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{
Id: link.Id,
AgentId: link.AgentId,
ProductId: link.ProductId,
ProductName: productNameMap[link.ProductId],
SetPrice: link.SetPrice,
ActualBasePrice: link.ActualBasePrice,
LinkIdentifier: link.LinkIdentifier,
CreateTime: link.CreateTime.Format("2006-01-02 15:04:05"),
})
}
resp = &types.AdminGetAgentLinkListResp{
Total: total,
Items: items,
}
return
}

View File

@@ -0,0 +1,136 @@
package admin_agent
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
"github.com/Masterminds/squirrel"
"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()
// 如果传入TeamLeaderId则查找该团队首领下的所有代理
if req.TeamLeaderId != nil {
builder = builder.Where(squirrel.Eq{"team_leader_id": *req.TeamLeaderId})
}
if req.Level != nil {
builder = builder.Where(squirrel.Eq{"level": *req.Level})
}
if req.Mobile != nil && *req.Mobile != "" {
// 加密手机号进行查询
encryptedMobile, err := crypto.EncryptMobile(*req.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
}
builder = builder.Where(squirrel.Eq{"mobile": encryptedMobile})
}
if req.Region != nil && *req.Region != "" {
// 注意region字段现在是可选的查询时需要处理NULL值
// 如果region字段是NULL使用IS NULL查询否则使用等值查询
// 这里简化处理直接使用等值查询如果数据库中有NULL值需要特殊处理
builder = builder.Where(squirrel.Eq{"region": *req.Region})
}
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 {
// 获取等级名称
levelName := ""
switch agent.Level {
case 1:
levelName = "普通"
case 2:
levelName = "黄金"
case 3:
levelName = "钻石"
}
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)
}
// 查询钱包信息
wallet, _ := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
// 查询实名认证信息
realNameInfo, _ := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agent.Id)
isRealName := false
if realNameInfo != nil && realNameInfo.VerifyTime.Valid {
isRealName = true // verify_time不为空表示已通过三要素核验
}
wechatId := ""
if agent.WechatId.Valid {
wechatId = agent.WechatId.String
}
teamLeaderId := int64(0)
if agent.TeamLeaderId.Valid {
teamLeaderId = agent.TeamLeaderId.Int64
}
// 获取区域
region := ""
if agent.Region.Valid {
region = agent.Region.String
}
item := types.AgentListItem{
Id: agent.Id,
UserId: agent.UserId,
Level: agent.Level,
LevelName: levelName,
Region: region,
Mobile: agent.Mobile,
WechatId: wechatId,
TeamLeaderId: teamLeaderId,
Balance: 0,
TotalEarnings: 0,
FrozenBalance: 0,
WithdrawnAmount: 0,
IsRealName: isRealName,
CreateTime: agent.CreateTime.Format("2006-01-02 15:04:05"),
}
if wallet != nil {
item.Balance = wallet.Balance
item.TotalEarnings = wallet.TotalEarnings
item.FrozenBalance = wallet.FrozenBalance
item.WithdrawnAmount = wallet.WithdrawnAmount
}
items = append(items, item)
}
resp = &types.AdminGetAgentListResp{
Total: total,
Items: items,
}
return
}

View File

@@ -0,0 +1,100 @@
package admin_agent
import (
"context"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentOrderListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentOrderListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentOrderListLogic {
return &AdminGetAgentOrderListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentOrderListLogic) AdminGetAgentOrderList(req *types.AdminGetAgentOrderListReq) (resp *types.AdminGetAgentOrderListResp, err error) {
builder := l.svcCtx.AgentOrderModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
if req.AgentId != nil {
builder = builder.Where("agent_id = ?", *req.AgentId)
}
if req.OrderId != nil {
builder = builder.Where("order_id = ?", *req.OrderId)
}
if req.ProcessStatus != nil {
builder = builder.Where("process_status = ?", *req.ProcessStatus)
}
// 分页查询
page := req.Page
if page <= 0 {
page = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
orders, total, err := l.svcCtx.AgentOrderModel.FindPageListByPageWithTotal(l.ctx, builder, page, pageSize, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理订单列表失败, %v", err)
}
// 批量查询产品名称
productIdSet := make(map[int64]struct{})
for _, order := range orders {
productIdSet[order.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.AgentOrderListItem, 0, len(orders))
for _, order := range orders {
items = append(items, types.AgentOrderListItem{
Id: order.Id,
AgentId: order.AgentId,
OrderId: order.OrderId,
ProductId: order.ProductId,
ProductName: productNameMap[order.ProductId],
OrderAmount: order.OrderAmount,
SetPrice: order.SetPrice,
ActualBasePrice: order.ActualBasePrice,
PriceCost: order.PriceCost,
AgentProfit: order.AgentProfit,
ProcessStatus: order.ProcessStatus,
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
})
}
return &types.AdminGetAgentOrderListResp{
Total: total,
Items: items,
}, nil
}

View File

@@ -0,0 +1,83 @@
package admin_agent
import (
"context"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentProductConfigListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentProductConfigListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentProductConfigListLogic {
return &AdminGetAgentProductConfigListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentProductConfigListLogic) AdminGetAgentProductConfigList(req *types.AdminGetAgentProductConfigListReq) (resp *types.AdminGetAgentProductConfigListResp, err error) {
builder := l.svcCtx.AgentProductConfigModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
if req.ProductId != nil {
builder = builder.Where("product_id = ?", *req.ProductId)
}
// 分页查询
page := req.Page
if page <= 0 {
page = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
configs, total, err := l.svcCtx.AgentProductConfigModel.FindPageListByPageWithTotal(l.ctx, builder, page, pageSize, "id DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询产品配置列表失败, %v", err)
}
// 组装响应
items := make([]types.AgentProductConfigItem, 0, len(configs))
for _, config := range configs {
priceThreshold := 0.0
if config.PriceThreshold.Valid {
priceThreshold = config.PriceThreshold.Float64
}
priceFeeRate := 0.0
if config.PriceFeeRate.Valid {
priceFeeRate = config.PriceFeeRate.Float64
}
items = append(items, types.AgentProductConfigItem{
Id: config.Id,
ProductId: config.ProductId,
ProductName: config.ProductName,
BasePrice: config.BasePrice,
PriceRangeMin: config.BasePrice, // 最低定价等于基础底价
PriceRangeMax: config.SystemMaxPrice,
PriceThreshold: priceThreshold,
PriceFeeRate: priceFeeRate,
CreateTime: config.CreateTime.Format("2006-01-02 15:04:05"),
})
}
return &types.AdminGetAgentProductConfigListResp{
Total: total,
Items: items,
}, nil
}

View File

@@ -0,0 +1,115 @@
package admin_agent
import (
"context"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentRealNameListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentRealNameListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentRealNameListLogic {
return &AdminGetAgentRealNameListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentRealNameListLogic) AdminGetAgentRealNameList(req *types.AdminGetAgentRealNameListReq) (resp *types.AdminGetAgentRealNameListResp, err error) {
builder := l.svcCtx.AgentRealNameModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
if req.AgentId != nil {
builder = builder.Where("agent_id = ?", *req.AgentId)
}
if req.Status != nil {
// 根据状态过滤1=未验证verify_time为NULL2=已通过verify_time不为NULL
switch *req.Status {
case 1: // 未验证
builder = builder.Where("verify_time IS NULL")
case 2: // 已通过
builder = builder.Where("verify_time IS NOT NULL")
}
}
// 分页查询
page := req.Page
if page <= 0 {
page = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
realNames, total, err := l.svcCtx.AgentRealNameModel.FindPageListByPageWithTotal(l.ctx, builder, page, pageSize, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询实名认证列表失败, %v", err)
}
// 组装响应
items := make([]types.AgentRealNameListItem, 0, len(realNames))
for _, realName := range realNames {
// 解密手机号(仅显示部分)
mobile := ""
if realName.Mobile != "" {
decrypted, err := crypto.DecryptMobile(realName.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
if err == nil {
mobile = decrypted
}
}
// 解密身份证号(仅显示部分)
idCard := ""
if realName.IdCard != "" {
decrypted, err := crypto.DecryptIDCard(realName.IdCard, []byte(l.svcCtx.Config.Encrypt.SecretKey))
if err == nil {
// 脱敏显示
if len(decrypted) > 10 {
idCard = decrypted[:3] + "***********" + decrypted[len(decrypted)-4:]
} else {
idCard = decrypted
}
}
}
// 根据verify_time判断状态NULL=未验证(1)不为NULL=已通过(2)
statusInt := int64(1) // 默认未验证
verifyTime := ""
if realName.VerifyTime.Valid {
statusInt = 2 // 已通过
verifyTime = realName.VerifyTime.Time.Format("2006-01-02 15:04:05")
}
item := types.AgentRealNameListItem{
Id: realName.Id,
AgentId: realName.AgentId,
Name: realName.Name,
IdCard: idCard,
Mobile: mobile,
Status: statusInt,
CreateTime: realName.CreateTime.Format("2006-01-02 15:04:05"),
}
// TODO: 重新生成接口后,取消注释下面的代码
item.VerifyTime = verifyTime
items = append(items, item)
}
return &types.AdminGetAgentRealNameListResp{
Total: total,
Items: items,
}, nil
}

View File

@@ -0,0 +1,95 @@
package admin_agent
import (
"context"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentRebateListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentRebateListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentRebateListLogic {
return &AdminGetAgentRebateListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentRebateListLogic) AdminGetAgentRebateList(req *types.AdminGetAgentRebateListReq) (resp *types.AdminGetAgentRebateListResp, err error) {
builder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
if req.AgentId != nil {
builder = builder.Where("agent_id = ?", *req.AgentId)
}
if req.SourceAgentId != nil {
builder = builder.Where("source_agent_id = ?", *req.SourceAgentId)
}
if req.RebateType != nil {
builder = builder.Where("rebate_type = ?", *req.RebateType)
}
// 分页查询
page := req.Page
if page <= 0 {
page = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
rebates, total, err := l.svcCtx.AgentRebateModel.FindPageListByPageWithTotal(l.ctx, builder, page, pageSize, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询返佣列表失败, %v", err)
}
// 批量查询产品名称
productIdSet := make(map[int64]struct{})
for _, rebate := range rebates {
productIdSet[rebate.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.AgentRebateListItem, 0, len(rebates))
for _, rebate := range rebates {
items = append(items, types.AgentRebateListItem{
Id: rebate.Id,
AgentId: rebate.AgentId,
SourceAgentId: rebate.SourceAgentId,
OrderId: rebate.OrderId,
RebateType: rebate.RebateType,
Amount: rebate.RebateAmount,
CreateTime: rebate.CreateTime.Format("2006-01-02 15:04:05"),
})
}
return &types.AdminGetAgentRebateListResp{
Total: total,
Items: items,
}, nil
}

View File

@@ -0,0 +1,79 @@
package admin_agent
import (
"context"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentUpgradeListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentUpgradeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentUpgradeListLogic {
return &AdminGetAgentUpgradeListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentUpgradeListLogic) AdminGetAgentUpgradeList(req *types.AdminGetAgentUpgradeListReq) (resp *types.AdminGetAgentUpgradeListResp, err error) {
builder := l.svcCtx.AgentUpgradeModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
if req.AgentId != nil {
builder = builder.Where("agent_id = ?", *req.AgentId)
}
if req.UpgradeType != nil {
builder = builder.Where("upgrade_type = ?", *req.UpgradeType)
}
if req.Status != nil {
builder = builder.Where("status = ?", *req.Status)
}
// 分页查询
page := req.Page
if page <= 0 {
page = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
upgrades, total, err := l.svcCtx.AgentUpgradeModel.FindPageListByPageWithTotal(l.ctx, builder, page, pageSize, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询升级记录列表失败, %v", err)
}
// 组装响应
items := make([]types.AgentUpgradeListItem, 0, len(upgrades))
for _, upgrade := range upgrades {
items = append(items, types.AgentUpgradeListItem{
Id: upgrade.Id,
AgentId: upgrade.AgentId,
FromLevel: upgrade.FromLevel,
ToLevel: upgrade.ToLevel,
UpgradeType: upgrade.UpgradeType,
UpgradeFee: upgrade.UpgradeFee,
RebateAmount: upgrade.RebateAmount,
Status: upgrade.Status,
CreateTime: upgrade.CreateTime.Format("2006-01-02 15:04:05"),
})
}
return &types.AdminGetAgentUpgradeListResp{
Total: total,
Items: items,
}, nil
}

View File

@@ -0,0 +1,59 @@
package admin_agent
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-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
}

View File

@@ -0,0 +1,128 @@
package admin_agent
import (
"context"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetInviteCodeListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetInviteCodeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetInviteCodeListLogic {
return &AdminGetInviteCodeListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetInviteCodeListLogic) AdminGetInviteCodeList(req *types.AdminGetInviteCodeListReq) (resp *types.AdminGetInviteCodeListResp, err error) {
// 1. 构建查询条件
builder := l.svcCtx.AgentInviteCodeModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
if req.Code != nil && *req.Code != "" {
builder = builder.Where("code = ?", *req.Code)
}
if req.AgentId != nil {
if *req.AgentId == 0 {
// agent_id = 0 表示查询平台发放的邀请码agent_id为NULL
builder = builder.Where("agent_id IS NULL")
} else {
builder = builder.Where("agent_id = ?", *req.AgentId)
}
}
if req.TargetLevel != nil {
builder = builder.Where("target_level = ?", *req.TargetLevel)
}
if req.Status != nil {
builder = builder.Where("status = ?", *req.Status)
}
// 2. 分页查询
list, total, err := l.svcCtx.AgentInviteCodeModel.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)
}
// 3. 批量查询代理信息(用于显示代理手机号)
agentIds := make(map[int64]struct{})
for _, v := range list {
if v.AgentId.Valid && v.AgentId.Int64 > 0 {
agentIds[v.AgentId.Int64] = struct{}{}
}
}
agentMobileMap := make(map[int64]string)
if len(agentIds) > 0 {
agentIdList := make([]int64, 0, len(agentIds))
for id := range agentIds {
agentIdList = append(agentIdList, id)
}
agents, _ := l.svcCtx.AgentModel.FindAll(l.ctx, l.svcCtx.AgentModel.SelectBuilder().Where(squirrel.Eq{"id": agentIdList}), "")
secretKey := l.svcCtx.Config.Encrypt.SecretKey
for _, agent := range agents {
mobile, decErr := crypto.DecryptMobile(agent.Mobile, secretKey)
if decErr == nil {
agentMobileMap[agent.Id] = mobile
} else {
l.Logger.Errorf("解密代理手机号失败: %v", decErr)
}
}
}
// 4. 格式化返回数据
items := make([]types.InviteCodeListItem, 0, len(list))
for _, v := range list {
item := types.InviteCodeListItem{
Id: v.Id,
Code: v.Code,
AgentId: 0,
AgentMobile: "",
TargetLevel: v.TargetLevel,
Status: v.Status,
CreateTime: v.CreateTime.Format("2006-01-02 15:04:05"),
}
if v.AgentId.Valid {
item.AgentId = v.AgentId.Int64
item.AgentMobile = agentMobileMap[v.AgentId.Int64]
}
if v.UsedUserId.Valid {
item.UsedUserId = v.UsedUserId.Int64
}
if v.UsedAgentId.Valid {
item.UsedAgentId = v.UsedAgentId.Int64
}
if v.UsedTime.Valid {
item.UsedTime = v.UsedTime.Time.Format("2006-01-02 15:04:05")
}
if v.ExpireTime.Valid {
item.ExpireTime = v.ExpireTime.Time.Format("2006-01-02 15:04:05")
}
if v.Remark.Valid {
item.Remark = v.Remark.String
}
items = append(items, item)
}
return &types.AdminGetInviteCodeListResp{
Total: total,
Items: items,
}, nil
}

View File

@@ -0,0 +1,67 @@
package admin_agent
import (
"context"
"strconv"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminUpdateAgentConfigLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateAgentConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateAgentConfigLogic {
return &AdminUpdateAgentConfigLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateAgentConfigLogic) AdminUpdateAgentConfig(req *types.AdminUpdateAgentConfigReq) (resp *types.AdminUpdateAgentConfigResp, err error) {
// 更新配置的辅助函数
updateConfig := func(key string, value *float64) error {
if value == nil {
return nil
}
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, key)
if err != nil {
return errors.Wrapf(err, "查询配置失败, key: %s", key)
}
config.ConfigValue = strconv.FormatFloat(*value, 'f', -1, 64)
return l.svcCtx.AgentConfigModel.UpdateWithVersion(l.ctx, nil, config)
}
// 更新各个配置项
if err := updateConfig("base_price", req.BasePrice); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新基础底价失败, %v", err)
}
if err := updateConfig("system_max_price", req.SystemMaxPrice); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新系统价格上限失败, %v", err)
}
if err := updateConfig("price_threshold", req.PriceThreshold); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提价标准阈值失败, %v", err)
}
if err := updateConfig("price_fee_rate", req.PriceFeeRate); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提价手续费比例失败, %v", err)
}
if err := updateConfig("tax_rate", req.TaxRate); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新税率失败, %v", err)
}
if err := updateConfig("tax_exemption_amount", req.TaxExemptionAmount); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新免税额度失败, %v", err)
}
return &types.AdminUpdateAgentConfigResp{
Success: true,
}, nil
}

View File

@@ -0,0 +1,51 @@
package admin_agent
import (
"context"
"database/sql"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminUpdateAgentProductConfigLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateAgentProductConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateAgentProductConfigLogic {
return &AdminUpdateAgentProductConfigLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateAgentProductConfigLogic) AdminUpdateAgentProductConfig(req *types.AdminUpdateAgentProductConfigReq) (resp *types.AdminUpdateAgentProductConfigResp, err error) {
// 查询配置
config, err := l.svcCtx.AgentProductConfigModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询产品配置失败, %v", err)
}
// 更新配置字段
config.BasePrice = req.BasePrice
config.SystemMaxPrice = req.PriceRangeMax
config.PriceThreshold = sql.NullFloat64{Float64: req.PriceThreshold, Valid: true}
config.PriceFeeRate = sql.NullFloat64{Float64: req.PriceFeeRate, Valid: true}
// 更新配置
if err := l.svcCtx.AgentProductConfigModel.UpdateWithVersion(l.ctx, nil, config); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新产品配置失败, %v", err)
}
return &types.AdminUpdateAgentProductConfigResp{
Success: true,
}, nil
}

View File

@@ -0,0 +1,70 @@
package admin_api
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminBatchUpdateApiStatusLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminBatchUpdateApiStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminBatchUpdateApiStatusLogic {
return &AdminBatchUpdateApiStatusLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminBatchUpdateApiStatusLogic) AdminBatchUpdateApiStatus(req *types.AdminBatchUpdateApiStatusReq) (resp *types.AdminBatchUpdateApiStatusResp, err error) {
// 1. 参数验证
if len(req.Ids) == 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API ID列表不能为空")
}
if req.Status != 0 && req.Status != 1 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"状态值无效, status: %d", req.Status)
}
// 2. 批量更新API状态
successCount := 0
for _, id := range req.Ids {
if id <= 0 {
continue
}
// 查询API是否存在
api, err := l.svcCtx.AdminApiModel.FindOne(l.ctx, id)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
continue // 跳过不存在的API
}
logx.Errorf("查询API失败, err: %v, id: %d", err, id)
continue
}
// 更新状态
api.Status = req.Status
_, err = l.svcCtx.AdminApiModel.Update(l.ctx, nil, api)
if err != nil {
logx.Errorf("更新API状态失败, err: %v, id: %d", err, id)
continue
}
successCount++
}
// 3. 返回结果
return &types.AdminBatchUpdateApiStatusResp{Success: true}, nil
}

View File

@@ -0,0 +1,78 @@
package admin_api
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminCreateApiLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminCreateApiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminCreateApiLogic {
return &AdminCreateApiLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminCreateApiLogic) AdminCreateApi(req *types.AdminCreateApiReq) (resp *types.AdminCreateApiResp, err error) {
// 1. 参数验证
if req.ApiName == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API名称不能为空")
}
if req.ApiCode == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API编码不能为空")
}
if req.Method == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"请求方法不能为空")
}
if req.Url == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API路径不能为空")
}
// 2. 检查API编码是否已存在
existing, err := l.svcCtx.AdminApiModel.FindOneByApiCode(l.ctx, req.ApiCode)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询API失败, err: %v, apiCode: %s", err, req.ApiCode)
}
if existing != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API编码已存在: %s", req.ApiCode)
}
// 3. 创建API记录
apiData := &model.AdminApi{
ApiName: req.ApiName,
ApiCode: req.ApiCode,
Method: req.Method,
Url: req.Url,
Status: req.Status,
Description: req.Description,
}
result, err := l.svcCtx.AdminApiModel.Insert(l.ctx, nil, apiData)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"创建API失败, err: %v", err)
}
// 4. 返回结果
apiId, _ := result.LastInsertId()
return &types.AdminCreateApiResp{Id: apiId}, nil
}

View File

@@ -0,0 +1,68 @@
package admin_api
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminDeleteApiLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminDeleteApiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminDeleteApiLogic {
return &AdminDeleteApiLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminDeleteApiLogic) AdminDeleteApi(req *types.AdminDeleteApiReq) (resp *types.AdminDeleteApiResp, err error) {
// 1. 参数验证
if req.Id <= 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API ID必须大于0, id: %d", req.Id)
}
// 2. 查询API是否存在
api, err := l.svcCtx.AdminApiModel.FindOne(l.ctx, req.Id)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"API不存在, id: %d", req.Id)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询API失败, err: %v, id: %d", err, req.Id)
}
// 3. 检查是否有角色关联该API
roleApiBuilder := l.svcCtx.AdminRoleApiModel.SelectBuilder().Where("api_id = ?", req.Id)
roleApis, err := l.svcCtx.AdminRoleApiModel.FindAll(l.ctx, roleApiBuilder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询角色API关联失败, err: %v, apiId: %d", err, req.Id)
}
if len(roleApis) > 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"该API已被角色使用无法删除, apiId: %d", req.Id)
}
// 4. 执行软删除
err = l.svcCtx.AdminApiModel.DeleteSoft(l.ctx, nil, api)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"删除API失败, err: %v, id: %d", err, req.Id)
}
// 5. 返回结果
return &types.AdminDeleteApiResp{Success: true}, nil
}

View File

@@ -0,0 +1,61 @@
package admin_api
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetApiDetailLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetApiDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetApiDetailLogic {
return &AdminGetApiDetailLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetApiDetailLogic) AdminGetApiDetail(req *types.AdminGetApiDetailReq) (resp *types.AdminGetApiDetailResp, err error) {
// 1. 参数验证
if req.Id <= 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API ID必须大于0, id: %d", req.Id)
}
// 2. 查询API详情
api, err := l.svcCtx.AdminApiModel.FindOne(l.ctx, req.Id)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"API不存在, id: %d", req.Id)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询API详情失败, err: %v, id: %d", err, req.Id)
}
// 3. 返回结果
return &types.AdminGetApiDetailResp{
AdminApiInfo: types.AdminApiInfo{
Id: api.Id,
ApiName: api.ApiName,
ApiCode: api.ApiCode,
Method: api.Method,
Url: api.Url,
Status: api.Status,
Description: api.Description,
CreateTime: api.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: api.UpdateTime.Format("2006-01-02 15:04:05"),
},
}, nil
}

View File

@@ -0,0 +1,89 @@
package admin_api
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetApiListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetApiListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetApiListLogic {
return &AdminGetApiListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetApiListLogic) AdminGetApiList(req *types.AdminGetApiListReq) (resp *types.AdminGetApiListResp, err error) {
// 1. 参数验证
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 20
}
if req.PageSize > 100 {
req.PageSize = 100
}
// 2. 构建查询条件
builder := l.svcCtx.AdminApiModel.SelectBuilder()
// 添加搜索条件
if req.ApiName != "" {
builder = builder.Where("api_name LIKE ?", "%"+req.ApiName+"%")
}
if req.Method != "" {
builder = builder.Where("method = ?", req.Method)
}
if req.Status > 0 {
builder = builder.Where("status = ?", req.Status)
}
// 3. 查询总数
total, err := l.svcCtx.AdminApiModel.FindCount(l.ctx, builder, "id")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询API总数失败, err: %v", err)
}
// 4. 查询列表
apis, err := l.svcCtx.AdminApiModel.FindPageListByPage(l.ctx, builder, req.Page, req.PageSize, "id DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询API列表失败, err: %v", err)
}
// 5. 转换数据格式
var apiList []types.AdminApiInfo
for _, api := range apis {
apiList = append(apiList, types.AdminApiInfo{
Id: api.Id,
ApiName: api.ApiName,
ApiCode: api.ApiCode,
Method: api.Method,
Url: api.Url,
Status: api.Status,
Description: api.Description,
CreateTime: api.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: api.UpdateTime.Format("2006-01-02 15:04:05"),
})
}
// 6. 返回结果
return &types.AdminGetApiListResp{
Items: apiList,
Total: total,
}, nil
}

View File

@@ -0,0 +1,92 @@
package admin_api
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminUpdateApiLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateApiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateApiLogic {
return &AdminUpdateApiLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateApiLogic) AdminUpdateApi(req *types.AdminUpdateApiReq) (resp *types.AdminUpdateApiResp, err error) {
// 1. 参数验证
if req.Id <= 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API ID必须大于0, id: %d", req.Id)
}
if req.ApiName == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API名称不能为空")
}
if req.ApiCode == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API编码不能为空")
}
if req.Method == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"请求方法不能为空")
}
if req.Url == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API路径不能为空")
}
// 2. 查询API是否存在
api, err := l.svcCtx.AdminApiModel.FindOne(l.ctx, req.Id)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"API不存在, id: %d", req.Id)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询API失败, err: %v, id: %d", err, req.Id)
}
// 3. 检查API编码是否被其他记录使用
if api.ApiCode != req.ApiCode {
existing, err := l.svcCtx.AdminApiModel.FindOneByApiCode(l.ctx, req.ApiCode)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询API失败, err: %v, apiCode: %s", err, req.ApiCode)
}
if existing != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API编码已存在: %s", req.ApiCode)
}
}
// 4. 更新API信息
api.ApiName = req.ApiName
api.ApiCode = req.ApiCode
api.Method = req.Method
api.Url = req.Url
api.Status = req.Status
api.Description = req.Description
_, err = l.svcCtx.AdminApiModel.Update(l.ctx, nil, api)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"更新API失败, err: %v, id: %d", err, req.Id)
}
// 5. 返回结果
return &types.AdminUpdateApiResp{Success: true}, nil
}

View File

@@ -0,0 +1,93 @@
package admin_auth
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
jwtx "ycc-server/common/jwt"
"ycc-server/common/xerr"
"ycc-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.AdminConfig.RefreshAfter
expiresAt := l.svcCtx.Config.AdminConfig.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.AdminConfig.AccessSecret, expiresAt)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrMsg("生成token失败"), "用户登录, 生成token失败, 用户名: %s", req.Username)
}
return &types.AdminLoginResp{
AccessToken: token,
AccessExpire: expiresAt,
RefreshAfter: refreshToken,
Roles: roles,
}, nil
}

View File

@@ -0,0 +1,92 @@
package admin_feature
import (
"context"
"encoding/hex"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminConfigFeatureExampleLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminConfigFeatureExampleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminConfigFeatureExampleLogic {
return &AdminConfigFeatureExampleLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminConfigFeatureExampleLogic) AdminConfigFeatureExample(req *types.AdminConfigFeatureExampleReq) (resp *types.AdminConfigFeatureExampleResp, err error) {
// 1. 验证功能是否存在
feature, err := l.svcCtx.FeatureModel.FindOne(l.ctx, req.FeatureId)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询功能失败, featureId: %d, err: %v", req.FeatureId, err)
}
if feature == nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR),
"功能不存在, featureId: %d", req.FeatureId)
}
// 2. 检查是否已存在示例数据
existingExample, err := l.svcCtx.ExampleModel.FindOneByFeatureId(l.ctx, req.FeatureId)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询示例数据失败, featureId: %d, err: %v", req.FeatureId, err)
}
// 3. 加密示例数据
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", decodeErr)
}
encryptedData, aesEncryptErr := crypto.AesEncrypt([]byte(req.Data), key)
if aesEncryptErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR),
"加密示例数据失败: %v", aesEncryptErr)
}
// 4. 准备示例数据
exampleData := &model.Example{
ApiId: feature.ApiId,
FeatureId: req.FeatureId,
Content: encryptedData,
}
// 4. 根据是否存在决定新增或更新
if existingExample == nil {
// 新增示例数据
_, err = l.svcCtx.ExampleModel.Insert(l.ctx, nil, exampleData)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"创建示例数据失败, featureId: %d, err: %v", req.FeatureId, err)
}
} else {
// 更新示例数据
exampleData.Id = existingExample.Id
exampleData.Version = existingExample.Version
_, err = l.svcCtx.ExampleModel.Update(l.ctx, nil, exampleData)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"更新示例数据失败, featureId: %d, err: %v", req.FeatureId, err)
}
}
// 5. 返回成功结果
return &types.AdminConfigFeatureExampleResp{Success: true}, nil
}

View File

@@ -0,0 +1,46 @@
package admin_feature
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-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
}

View File

@@ -0,0 +1,45 @@
package admin_feature
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,46 @@
package admin_feature
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,82 @@
package admin_feature
import (
"context"
"encoding/hex"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetFeatureExampleLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetFeatureExampleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetFeatureExampleLogic {
return &AdminGetFeatureExampleLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetFeatureExampleLogic) AdminGetFeatureExample(req *types.AdminGetFeatureExampleReq) (resp *types.AdminGetFeatureExampleResp, err error) {
// 1. 查询示例数据
example, err := l.svcCtx.ExampleModel.FindOneByFeatureId(l.ctx, req.FeatureId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 示例数据不存在,返回空数据
return &types.AdminGetFeatureExampleResp{
Id: 0,
FeatureId: req.FeatureId,
ApiId: "",
Data: "",
CreateTime: "",
UpdateTime: "",
}, nil
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询示例数据失败, featureId: %d, err: %v", req.FeatureId, err)
}
// 2. 获取解密密钥
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", decodeErr)
}
// 3. 解密示例数据
var decryptedData string
if example.Content == "000" {
// 特殊值,直接返回
decryptedData = example.Content
} else {
// 解密数据
decryptedBytes, decryptErr := crypto.AesDecrypt(example.Content, key)
if decryptErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR),
"解密示例数据失败: %v", decryptErr)
}
decryptedData = string(decryptedBytes)
}
// 4. 返回解密后的数据
return &types.AdminGetFeatureExampleResp{
Id: example.Id,
FeatureId: example.FeatureId,
ApiId: example.ApiId,
Data: decryptedData,
CreateTime: example.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: example.UpdateTime.Format("2006-01-02 15:04:05"),
}, nil
}

View File

@@ -0,0 +1,66 @@
package admin_feature
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,59 @@
package admin_feature
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"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) {
// 1. 参数验证
if req.Id <= 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"功能ID必须大于0, id: %d", req.Id)
}
// 2. 查询记录是否存在
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)
}
// 3. 直接更新record的字段只更新非空字段
if req.ApiId != nil && *req.ApiId != "" {
record.ApiId = *req.ApiId
}
if req.Name != nil && *req.Name != "" {
record.Name = *req.Name
}
// 4. 执行更新操作
err = l.svcCtx.FeatureModel.UpdateWithVersion(l.ctx, nil, record)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"更新功能失败, err: %v, id: %d", err, req.Id)
}
// 5. 返回成功结果
return &types.AdminUpdateFeatureResp{Success: true}, nil
}

View File

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

View File

@@ -0,0 +1,75 @@
package admin_menu
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"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) {
// 1. 参数验证
if req.Id <= 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"菜单ID必须大于0, id: %d", req.Id)
}
// 2. 查询菜单是否存在
menu, err := l.svcCtx.AdminMenuModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查找菜单失败, err: %v, id: %d", err, req.Id)
}
// 3. 检查是否有子菜单
childMenuBuilder := l.svcCtx.AdminMenuModel.SelectBuilder().Where("pid = ?", req.Id)
childMenus, err := l.svcCtx.AdminMenuModel.FindAll(l.ctx, childMenuBuilder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询子菜单失败, err: %v, parent_id: %d", err, req.Id)
}
if len(childMenus) > 0 {
return nil, errors.Wrapf(xerr.NewErrMsg("该菜单下还有子菜单,无法删除"),
"该菜单下还有子菜单,无法删除, id: %d", req.Id)
}
// 4. 检查是否有角色关联该菜单
roleMenuBuilder := l.svcCtx.AdminRoleMenuModel.SelectBuilder().Where("menu_id = ?", req.Id)
roleMenus, err := l.svcCtx.AdminRoleMenuModel.FindAll(l.ctx, roleMenuBuilder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询角色菜单关联失败, err: %v, menu_id: %d", err, req.Id)
}
if len(roleMenus) > 0 {
return nil, errors.Wrapf(xerr.NewErrMsg("该菜单已被角色使用,无法删除"),
"该菜单已被角色使用,无法删除, id: %d", req.Id)
}
// 5. 执行软删除
err = l.svcCtx.AdminMenuModel.DeleteSoft(l.ctx, nil, menu)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"删除菜单失败, err: %v, id: %d", err, req.Id)
}
// 6. 返回成功结果
return &types.DeleteMenuResp{Success: true}, nil
}

View File

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

View File

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

View File

@@ -0,0 +1,109 @@
package admin_menu
import (
"context"
"encoding/json"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

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

View File

@@ -0,0 +1,50 @@
package admin_notification
import (
"context"
"database/sql"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-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
}

View File

@@ -0,0 +1,38 @@
package admin_notification
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,53 @@
package admin_notification
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,82 @@
package admin_notification
import (
"context"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,66 @@
package admin_notification
import (
"context"
"database/sql"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,99 @@
package admin_order
import (
"context"
"database/sql"
"fmt"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-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
}

View File

@@ -0,0 +1,63 @@
package admin_order
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,136 @@
package admin_order
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/globalkey"
"ycc-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 isAgentOrder bool
var agentProcessStatus string
agentOrder, err := l.svcCtx.AgentOrderModel.FindOneByOrderId(l.ctx, order.Id)
if err == nil && agentOrder != nil {
isAgentOrder = true
// 查询代理佣金记录
commissions, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx,
l.svcCtx.AgentCommissionModel.SelectBuilder().Where("order_id = ?", order.Id), "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderDetail, 查询代理佣金失败 err: %v", err)
}
if len(commissions) > 0 {
agentProcessStatus = "success"
} else {
// 检查订单状态,如果是已支付但无佣金记录,则为待处理或失败
if order.Status == "paid" {
agentProcessStatus = "pending"
} else {
agentProcessStatus = "failed"
}
}
} else {
isAgentOrder = false
agentProcessStatus = "not_agent"
}
// 获取查询状态
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,
IsAgentOrder: isAgentOrder,
AgentProcessStatus: agentProcessStatus,
}
// 处理可选字段
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
}

View File

@@ -0,0 +1,295 @@
package admin_order
import (
"context"
"sync"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/globalkey"
"ycc-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)
agentOrderMap := make(map[int64]bool) // 代理订单映射
agentProcessStatusMap := 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] = "" // 未知状态保持为空字符串
}
}
}
// 批量获取代理订单状态
agentOrders, err := l.svcCtx.AgentOrderModel.FindAll(l.ctx,
l.svcCtx.AgentOrderModel.SelectBuilder().Where(squirrel.Eq{"order_id": orderIds}), "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 批量查询代理订单失败 err: %v", err)
}
// 记录代理订单
for _, agentOrder := range agentOrders {
agentOrderMap[agentOrder.OrderId] = true
}
// 对于代理订单,查询代理处理状态
if len(agentOrders) > 0 {
agentOrderIds := make([]int64, 0, len(agentOrders))
for _, agentOrder := range agentOrders {
agentOrderIds = append(agentOrderIds, agentOrder.OrderId)
}
// 查询代理佣金记录
commissions, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx,
l.svcCtx.AgentCommissionModel.SelectBuilder().Where(squirrel.Eq{"order_id": agentOrderIds}), "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 批量查询代理佣金失败 err: %v", err)
}
// 记录有佣金记录的订单为处理成功
processedOrderIds := make(map[int64]bool)
for _, commission := range commissions {
processedOrderIds[commission.OrderId] = true
}
// 创建订单状态映射,避免重复查找
orderStatusMap := make(map[int64]string)
for _, order := range orders {
orderStatusMap[order.Id] = order.Status
}
// 设置代理处理状态
for _, agentOrder := range agentOrders {
orderId := agentOrder.OrderId
if processedOrderIds[orderId] {
agentProcessStatusMap[orderId] = "success"
} else {
// 检查订单状态,如果是已支付但无佣金记录,则为待处理或失败
if orderStatusMap[orderId] == "paid" {
agentProcessStatusMap[orderId] = "pending"
} else {
agentProcessStatusMap[orderId] = "failed"
}
}
}
}
}
// 并发获取产品信息
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
}
// 设置代理订单相关字段
if agentOrderMap[order.Id] {
item.IsAgentOrder = true
item.AgentProcessStatus = agentProcessStatusMap[order.Id]
} else {
item.IsAgentOrder = false
item.AgentProcessStatus = "not_agent"
}
resp.Items = append(resp.Items, item)
}
return resp, nil
}

View File

@@ -0,0 +1,193 @@
package admin_order
import (
"context"
"database/sql"
"fmt"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
const (
PaymentPlatformAlipay = "alipay"
PaymentPlatformWechat = "wechat"
OrderStatusPaid = "paid"
RefundNoPrefix = "refund-"
)
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.getAndValidateOrder(req.Id, req.RefundAmount)
if err != nil {
return nil, err
}
// 根据支付平台处理退款
switch order.PaymentPlatform {
case PaymentPlatformAlipay:
return l.handleAlipayRefund(order, req)
case PaymentPlatformWechat:
return l.handleWechatRefund(order, req)
default:
return nil, errors.Wrapf(xerr.NewErrMsg("不支持的支付平台"), "AdminRefundOrder, 不支持的支付平台: %s", order.PaymentPlatform)
}
}
// getAndValidateOrder 获取并验证订单信息
func (l *AdminRefundOrderLogic) getAndValidateOrder(orderId int64, refundAmount float64) (*model.Order, error) {
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, orderId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminRefundOrder, 查询订单失败 err: %v", err)
}
// 检查订单状态
if order.Status != OrderStatusPaid {
return nil, errors.Wrapf(xerr.NewErrMsg("订单状态不正确,无法退款"), "AdminRefundOrder, 订单状态: %s", order.Status)
}
// 检查退款金额
if refundAmount > order.Amount {
return nil, errors.Wrapf(xerr.NewErrMsg("退款金额不能大于订单金额"), "AdminRefundOrder, 退款金额: %f, 订单金额: %f", refundAmount, order.Amount)
}
return order, nil
}
// handleAlipayRefund 处理支付宝退款
func (l *AdminRefundOrderLogic) handleAlipayRefund(order *model.Order, req *types.AdminRefundOrderReq) (*types.AdminRefundOrderResp, error) {
// 调用支付宝退款接口
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)
}
refundNo := l.generateRefundNo(order.OrderNo)
if refundResp.IsSuccess() {
// 支付宝退款成功,创建成功记录
err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, refundResp.TradeNo, model.OrderStatusRefunded, model.OrderRefundStatusSuccess)
if err != nil {
return nil, err
}
return &types.AdminRefundOrderResp{
Status: model.OrderStatusRefunded,
RefundNo: refundNo,
Amount: req.RefundAmount,
}, nil
} else {
// 支付宝退款失败,创建失败记录但不更新订单状态
err = l.createRefundRecordOnly(order, req, refundNo, refundResp.TradeNo, model.OrderRefundStatusFailed)
if err != nil {
logx.Errorf("创建退款失败记录时出错: %v", err)
}
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("退款失败: %v", refundResp.Msg)), "AdminRefundOrder, 支付宝退款失败")
}
}
// handleWechatRefund 处理微信退款
func (l *AdminRefundOrderLogic) handleWechatRefund(order *model.Order, req *types.AdminRefundOrderReq) (*types.AdminRefundOrderResp, error) {
// 调用微信退款接口
err := l.svcCtx.WechatPayService.WeChatRefund(l.ctx, order.OrderNo, req.RefundAmount, order.Amount)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 微信退款失败 err: %v", err)
}
// 微信退款是异步的创建pending状态的退款记录
refundNo := l.generateRefundNo(order.OrderNo)
err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, "", model.OrderStatusRefunding, model.OrderRefundStatusPending)
if err != nil {
return nil, err
}
return &types.AdminRefundOrderResp{
Status: model.OrderRefundStatusPending,
RefundNo: refundNo,
Amount: req.RefundAmount,
}, nil
}
// createRefundRecordAndUpdateOrder 创建退款记录并更新订单状态
func (l *AdminRefundOrderLogic) createRefundRecordAndUpdateOrder(order *model.Order, req *types.AdminRefundOrderReq, refundNo, platformRefundId, orderStatus, refundStatus string) error {
return l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 创建退款记录
refund := &model.OrderRefund{
RefundNo: refundNo,
PlatformRefundId: l.createNullString(platformRefundId),
OrderId: order.Id,
UserId: order.UserId,
ProductId: order.ProductId,
RefundAmount: req.RefundAmount,
RefundReason: l.createNullString(req.RefundReason),
Status: refundStatus, // 使用传入的状态,不再硬编码
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 = orderStatus
if _, err := l.svcCtx.OrderModel.Update(ctx, session, order); err != nil {
return fmt.Errorf("更新订单状态失败: %v", err)
}
return nil
})
}
// createRefundRecordOnly 仅创建退款记录,不更新订单状态(用于退款失败的情况)
func (l *AdminRefundOrderLogic) createRefundRecordOnly(order *model.Order, req *types.AdminRefundOrderReq, refundNo, platformRefundId, refundStatus string) error {
refund := &model.OrderRefund{
RefundNo: refundNo,
PlatformRefundId: l.createNullString(platformRefundId),
OrderId: order.Id,
UserId: order.UserId,
ProductId: order.ProductId,
RefundAmount: req.RefundAmount,
RefundReason: l.createNullString(req.RefundReason),
Status: refundStatus,
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
}
_, err := l.svcCtx.OrderRefundModel.Insert(l.ctx, nil, refund)
if err != nil {
return fmt.Errorf("创建退款记录失败: %v", err)
}
return nil
}
// generateRefundNo 生成退款单号
func (l *AdminRefundOrderLogic) generateRefundNo(orderNo string) string {
return fmt.Sprintf("%s%s", RefundNoPrefix, orderNo)
}
// createNullString 创建 sql.NullString
func (l *AdminRefundOrderLogic) createNullString(value string) sql.NullString {
return sql.NullString{
String: value,
Valid: value != "",
}
}

View File

@@ -0,0 +1,63 @@
package admin_order
import (
"context"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminRetryAgentProcessLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminRetryAgentProcessLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminRetryAgentProcessLogic {
return &AdminRetryAgentProcessLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminRetryAgentProcessLogic) AdminRetryAgentProcess(req *types.AdminRetryAgentProcessReq) (resp *types.AdminRetryAgentProcessResp, err error) {
// 新系统查询订单并调用AgentProcess重新处理
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, req.Id)
if err != nil {
return &types.AdminRetryAgentProcessResp{
Status: "failed",
Message: "订单不存在",
ProcessedAt: time.Now().Format("2006-01-02 15:04:05"),
}, nil
}
err = l.svcCtx.AgentService.AgentProcess(l.ctx, order)
if err != nil {
// 检查是否是"已经处理"的错误
if err.Error() == "代理处理已经成功,无需重新执行" {
return &types.AdminRetryAgentProcessResp{
Status: "already_processed",
Message: "代理处理已经成功,无需重新执行",
ProcessedAt: time.Now().Format("2006-01-02 15:04:05"),
}, nil
}
// 其他错误
logx.Errorf("重新执行代理处理失败订单ID: %d, 错误: %v", req.Id, err)
return &types.AdminRetryAgentProcessResp{
Status: "failed",
Message: err.Error(),
ProcessedAt: time.Now().Format("2006-01-02 15:04:05"),
}, nil
}
// 执行成功
return &types.AdminRetryAgentProcessResp{
Status: "success",
Message: "代理处理重新执行成功",
ProcessedAt: time.Now().Format("2006-01-02 15:04:05"),
}, nil
}

View File

@@ -0,0 +1,113 @@
package admin_order
import (
"context"
"database/sql"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-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
}

View File

@@ -0,0 +1,57 @@
package admin_platform_user
import (
"context"
"database/sql"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-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
}

View File

@@ -0,0 +1,43 @@
package admin_platform_user
import (
"context"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,53 @@
package admin_platform_user
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"ycc-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
}

View File

@@ -0,0 +1,88 @@
package admin_platform_user
import (
"context"
"database/sql"
"fmt"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"ycc-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
}

View File

@@ -0,0 +1,64 @@
package admin_platform_user
import (
"context"
"database/sql"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"ycc-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
}

View File

@@ -0,0 +1,50 @@
package admin_product
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"database/sql"
"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
}

View File

@@ -0,0 +1,44 @@
package admin_product
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,49 @@
package admin_product
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,119 @@
package admin_product
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-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
}

View File

@@ -0,0 +1,69 @@
package admin_product
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,159 @@
package admin_product
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"sync"
"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
}

View File

@@ -0,0 +1,65 @@
package admin_product
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"database/sql"
"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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,166 @@
package admin_promotion
import (
"context"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-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
}

View File

@@ -0,0 +1,57 @@
package admin_promotion
import (
"context"
"fmt"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

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

View File

@@ -0,0 +1,62 @@
package admin_query
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/globalkey"
"ycc-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
}

View File

@@ -0,0 +1,126 @@
package admin_query
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"sync"
"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
}

View File

@@ -0,0 +1,88 @@
package admin_query
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/globalkey"
"ycc-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
}

View File

@@ -0,0 +1,189 @@
package admin_query
import (
"context"
"database/sql"
"encoding/hex"
"encoding/json"
"fmt"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
"ycc-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
}

View File

@@ -0,0 +1,63 @@
package admin_query
import (
"context"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,96 @@
package admin_role_api
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminAssignRoleApiLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminAssignRoleApiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminAssignRoleApiLogic {
return &AdminAssignRoleApiLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminAssignRoleApiLogic) AdminAssignRoleApi(req *types.AdminAssignRoleApiReq) (resp *types.AdminAssignRoleApiResp, err error) {
// 1. 参数验证
if req.RoleId <= 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"角色ID必须大于0, roleId: %d", req.RoleId)
}
if len(req.ApiIds) == 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API ID列表不能为空")
}
// 2. 查询角色是否存在
_, err = l.svcCtx.AdminRoleModel.FindOne(l.ctx, req.RoleId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"角色不存在, roleId: %d", req.RoleId)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询角色失败, err: %v, roleId: %d", err, req.RoleId)
}
// 3. 批量分配API权限
successCount := 0
for _, apiId := range req.ApiIds {
if apiId <= 0 {
continue
}
// 检查API是否存在
_, err := l.svcCtx.AdminApiModel.FindOne(l.ctx, apiId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
logx.Errorf("API不存在, apiId: %d", apiId)
continue
}
logx.Errorf("查询API失败, err: %v, apiId: %d", err, apiId)
continue
}
// 检查是否已存在关联
existing, err := l.svcCtx.AdminRoleApiModel.FindOneByRoleIdApiId(l.ctx, req.RoleId, apiId)
if err != nil && !errors.Is(err, model.ErrNotFound) {
logx.Errorf("查询角色API关联失败, err: %v, roleId: %d, apiId: %d", err, req.RoleId, apiId)
continue
}
if existing != nil {
continue // 已存在,跳过
}
// 创建关联
roleApiData := &model.AdminRoleApi{
RoleId: req.RoleId,
ApiId: apiId,
}
_, err = l.svcCtx.AdminRoleApiModel.Insert(l.ctx, nil, roleApiData)
if err != nil {
logx.Errorf("创建角色API关联失败, err: %v, roleId: %d, apiId: %d", err, req.RoleId, apiId)
continue
}
successCount++
}
// 4. 返回结果
return &types.AdminAssignRoleApiResp{Success: true}, nil
}

View File

@@ -0,0 +1,64 @@
package admin_role_api
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAllApiListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAllApiListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAllApiListLogic {
return &AdminGetAllApiListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAllApiListLogic) AdminGetAllApiList(req *types.AdminGetAllApiListReq) (resp *types.AdminGetAllApiListResp, err error) {
// 1. 构建查询条件
builder := l.svcCtx.AdminApiModel.SelectBuilder()
// 添加状态过滤
if req.Status > 0 {
builder = builder.Where("status = ?", req.Status)
}
// 2. 查询所有API列表
apis, err := l.svcCtx.AdminApiModel.FindAll(l.ctx, builder, "id ASC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询API列表失败, err: %v", err)
}
// 3. 转换数据格式
var apiList []types.AdminRoleApiInfo
for _, api := range apis {
apiList = append(apiList, types.AdminRoleApiInfo{
Id: 0, // 这里不是关联ID而是API ID
RoleId: 0, // 这里不是角色ID
ApiId: api.Id,
ApiName: api.ApiName,
ApiCode: api.ApiCode,
Method: api.Method,
Url: api.Url,
Status: api.Status,
Description: api.Description,
})
}
// 4. 返回结果
return &types.AdminGetAllApiListResp{
Items: apiList,
}, nil
}

View File

@@ -0,0 +1,84 @@
package admin_role_api
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetRoleApiListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetRoleApiListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetRoleApiListLogic {
return &AdminGetRoleApiListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetRoleApiListLogic) AdminGetRoleApiList(req *types.AdminGetRoleApiListReq) (resp *types.AdminGetRoleApiListResp, err error) {
// 1. 参数验证
if req.RoleId <= 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"角色ID必须大于0, roleId: %d", req.RoleId)
}
// 2. 查询角色是否存在
_, err = l.svcCtx.AdminRoleModel.FindOne(l.ctx, req.RoleId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"角色不存在, roleId: %d", req.RoleId)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询角色失败, err: %v, roleId: %d", err, req.RoleId)
}
// 3. 查询角色API权限列表
builder := l.svcCtx.AdminRoleApiModel.SelectBuilder().
Where("role_id = ?", req.RoleId)
roleApis, err := l.svcCtx.AdminRoleApiModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询角色API权限失败, err: %v, roleId: %d", err, req.RoleId)
}
// 4. 转换数据格式
var apiList []types.AdminRoleApiInfo
for _, roleApi := range roleApis {
// 查询API详情
api, err := l.svcCtx.AdminApiModel.FindOne(l.ctx, roleApi.ApiId)
if err != nil {
logx.Errorf("查询API详情失败, err: %v, apiId: %d", err, roleApi.ApiId)
continue
}
apiList = append(apiList, types.AdminRoleApiInfo{
Id: roleApi.Id,
RoleId: roleApi.RoleId,
ApiId: roleApi.ApiId,
ApiName: api.ApiName,
ApiCode: api.ApiCode,
Method: api.Method,
Url: api.Url,
Status: api.Status,
Description: api.Description,
})
}
// 5. 返回结果
return &types.AdminGetRoleApiListResp{
Items: apiList,
}, nil
}

View File

@@ -0,0 +1,80 @@
package admin_role_api
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminRemoveRoleApiLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminRemoveRoleApiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminRemoveRoleApiLogic {
return &AdminRemoveRoleApiLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminRemoveRoleApiLogic) AdminRemoveRoleApi(req *types.AdminRemoveRoleApiReq) (resp *types.AdminRemoveRoleApiResp, err error) {
// 1. 参数验证
if req.RoleId <= 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"角色ID必须大于0, roleId: %d", req.RoleId)
}
if len(req.ApiIds) == 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API ID列表不能为空")
}
// 2. 查询角色是否存在
_, err = l.svcCtx.AdminRoleModel.FindOne(l.ctx, req.RoleId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"角色不存在, roleId: %d", req.RoleId)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询角色失败, err: %v, roleId: %d", err, req.RoleId)
}
// 3. 批量移除API权限
successCount := 0
for _, apiId := range req.ApiIds {
if apiId <= 0 {
continue
}
// 查询关联记录
roleApi, err := l.svcCtx.AdminRoleApiModel.FindOneByRoleIdApiId(l.ctx, req.RoleId, apiId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
continue // 不存在,跳过
}
logx.Errorf("查询角色API关联失败, err: %v, roleId: %d, apiId: %d", err, req.RoleId, apiId)
continue
}
// 删除关联
err = l.svcCtx.AdminRoleApiModel.DeleteSoft(l.ctx, nil, roleApi)
if err != nil {
logx.Errorf("删除角色API关联失败, err: %v, roleId: %d, apiId: %d", err, req.RoleId, apiId)
continue
}
successCount++
}
// 4. 返回结果
return &types.AdminRemoveRoleApiResp{Success: true}, nil
}

View File

@@ -0,0 +1,96 @@
package admin_role_api
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminUpdateRoleApiLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateRoleApiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateRoleApiLogic {
return &AdminUpdateRoleApiLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateRoleApiLogic) AdminUpdateRoleApi(req *types.AdminUpdateRoleApiReq) (resp *types.AdminUpdateRoleApiResp, err error) {
// 1. 参数验证
if req.RoleId <= 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"角色ID必须大于0, roleId: %d", req.RoleId)
}
// 2. 查询角色是否存在
_, err = l.svcCtx.AdminRoleModel.FindOne(l.ctx, req.RoleId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"角色不存在, roleId: %d", req.RoleId)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询角色失败, err: %v, roleId: %d", err, req.RoleId)
}
// 3. 删除该角色的所有API权限
builder := l.svcCtx.AdminRoleApiModel.SelectBuilder().Where("role_id = ?", req.RoleId)
roleApis, err := l.svcCtx.AdminRoleApiModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询角色API权限失败, err: %v, roleId: %d", err, req.RoleId)
}
// 删除现有权限
for _, roleApi := range roleApis {
err = l.svcCtx.AdminRoleApiModel.DeleteSoft(l.ctx, nil, roleApi)
if err != nil {
logx.Errorf("删除角色API权限失败, err: %v, roleId: %d, apiId: %d", err, req.RoleId, roleApi.ApiId)
}
}
// 4. 添加新的API权限
if len(req.ApiIds) > 0 {
for _, apiId := range req.ApiIds {
if apiId <= 0 {
continue
}
// 检查API是否存在
_, err := l.svcCtx.AdminApiModel.FindOne(l.ctx, apiId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
logx.Errorf("API不存在, apiId: %d", apiId)
continue
}
logx.Errorf("查询API失败, err: %v, apiId: %d", err, apiId)
continue
}
// 创建新的关联
roleApiData := &model.AdminRoleApi{
RoleId: req.RoleId,
ApiId: apiId,
}
_, err = l.svcCtx.AdminRoleApiModel.Insert(l.ctx, nil, roleApiData)
if err != nil {
logx.Errorf("创建角色API关联失败, err: %v, roleId: %d, apiId: %d", err, req.RoleId, apiId)
}
}
}
// 5. 返回结果
return &types.AdminUpdateRoleApiResp{Success: true}, nil
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,149 @@
package admin_user
import (
"context"
"sync"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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
}

View File

@@ -0,0 +1,63 @@
package admin_user
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminResetPasswordLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminResetPasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminResetPasswordLogic {
return &AdminResetPasswordLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminResetPasswordLogic) AdminResetPassword(req *types.AdminResetPasswordReq) (resp *types.AdminResetPasswordResp, err error) {
// 检查用户是否存在
user, err := l.svcCtx.AdminUserModel.FindOne(l.ctx, req.Id)
if err != nil {
if err == model.ErrNotFound {
return nil, errors.Wrapf(xerr.NewErrMsg("用户不存在"), "用户ID: %d", req.Id)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户失败: %v", err)
}
// 检查用户状态
if user.Status != 1 {
return nil, errors.Wrapf(xerr.NewErrMsg("用户已被禁用,无法重置密码"), "用户ID: %d", req.Id)
}
// 对密码进行加密
hashedPassword, err := crypto.PasswordHash(req.Password)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "密码加密失败: %v", err)
}
// 更新用户密码
user.Password = hashedPassword
_, err = l.svcCtx.AdminUserModel.Update(l.ctx, nil, user)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新密码失败: %v", err)
}
l.Infof("管理员密码重置成功用户ID: %d, 用户名: %s", req.Id, user.Username)
return &types.AdminResetPasswordResp{
Success: true,
}, nil
}

View File

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

View File

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

View File

@@ -0,0 +1,294 @@
package agent
import (
"context"
"database/sql"
"fmt"
"time"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"ycc-server/app/main/api/internal/svc"
"ycc-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)
}
// 1. 必须提供邀请码,用户不能自主成为代理
if req.InviteCode == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("必须提供邀请码才能成为代理,请联系平台或代理获取邀请码"), "")
}
// 2. 校验验证码
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("验证码已过期"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "读取验证码失败, %v", err)
}
if cacheCode != req.Code {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "")
}
}
var userID int64
transErr := l.svcCtx.AgentModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
// 1. 处理用户注册/绑定
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), "查询用户失败, %v", 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
}
// 3. 检查是否已是代理
existingAgent, err := l.svcCtx.AgentModel.FindOneByUserId(transCtx, userID)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
}
if existingAgent != nil {
return errors.Wrapf(xerr.NewErrMsg("您已经是代理"), "")
}
// 4. 必须通过邀请码成为代理(没有其他途径)
// 4.1 查询邀请码
inviteCodeModel, err := l.svcCtx.AgentInviteCodeModel.FindOneByCode(transCtx, req.InviteCode)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return errors.Wrapf(xerr.NewErrMsg("邀请码不存在"), "")
}
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询邀请码失败, %v", err)
}
// 4.2 验证邀请码状态
// 钻石级别的邀请码只能使用一次,使用后立即失效
// 普通级别的邀请码可以无限使用
if inviteCodeModel.Status != 0 {
if inviteCodeModel.Status == 1 {
return errors.Wrapf(xerr.NewErrMsg("邀请码已使用"), "")
}
return errors.Wrapf(xerr.NewErrMsg("邀请码已失效"), "")
}
// 4.3 验证邀请码是否过期
if inviteCodeModel.ExpireTime.Valid && inviteCodeModel.ExpireTime.Time.Before(time.Now()) {
return errors.Wrapf(xerr.NewErrMsg("邀请码已过期"), "")
}
// 4.4 获取邀请码信息
targetLevel := inviteCodeModel.TargetLevel
var parentAgentId int64 = 0
if inviteCodeModel.AgentId.Valid {
parentAgentId = inviteCodeModel.AgentId.Int64
}
// 4.5 创建代理记录
newAgent := &model.Agent{
UserId: userID,
Level: targetLevel,
Mobile: encryptedMobile,
}
if req.Region != "" {
newAgent.Region = sql.NullString{String: req.Region, Valid: true}
}
// 4.6 处理上级关系
if parentAgentId > 0 {
// 代理发放的邀请码,成为该代理的下级
parentAgent, err := l.svcCtx.AgentModel.FindOne(transCtx, parentAgentId)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询上级代理失败, %v", err)
}
// 验证关系是否允许
if newAgent.Level > parentAgent.Level {
return errors.Wrapf(xerr.NewErrMsg("代理等级不能高于上级代理"), "")
}
// 查找团队首领
teamLeaderId, err := l.findTeamLeader(transCtx, parentAgent.Id)
if err != nil {
return errors.Wrapf(err, "查找团队首领失败")
}
if teamLeaderId > 0 {
newAgent.TeamLeaderId = sql.NullInt64{Int64: teamLeaderId, Valid: true}
}
// 先插入代理记录
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
if err != nil {
return errors.Wrapf(err, "创建代理记录失败")
}
agentId, _ := agentResult.LastInsertId()
newAgent.Id = agentId
// 建立关系
relation := &model.AgentRelation{
ParentId: parentAgent.Id,
ChildId: agentId,
RelationType: 1, // 直接关系
}
if _, err := l.svcCtx.AgentRelationModel.Insert(transCtx, session, relation); err != nil {
return errors.Wrapf(err, "建立代理关系失败")
}
} else {
// 平台发放的钻石邀请码,独立成团队
if targetLevel == 3 {
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
if err != nil {
return errors.Wrapf(err, "创建代理记录失败")
}
agentId, _ := agentResult.LastInsertId()
newAgent.Id = agentId
// 设置自己为团队首领
newAgent.TeamLeaderId = sql.NullInt64{Int64: agentId, Valid: true}
if err := l.svcCtx.AgentModel.UpdateWithVersion(transCtx, session, newAgent); err != nil {
return errors.Wrapf(err, "更新团队首领失败")
}
} else {
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
if err != nil {
return errors.Wrapf(err, "创建代理记录失败")
}
_, _ = agentResult.LastInsertId()
}
}
// 4.7 初始化钱包
wallet := &model.AgentWallet{
AgentId: newAgent.Id,
}
if _, err := l.svcCtx.AgentWalletModel.Insert(transCtx, session, wallet); err != nil {
return errors.Wrapf(err, "初始化钱包失败")
}
// 4.8 更新邀请码状态
// 钻石级别的邀请码只能使用一次,使用后立即失效
// 普通级别的邀请码可以无限使用,不更新状态
if targetLevel == 3 {
// 钻石邀请码:使用后失效
inviteCodeModel.Status = 1 // 已使用(使用后立即失效)
}
// 记录使用信息(用于统计,普通邀请码可以多次使用)
inviteCodeModel.UsedUserId = sql.NullInt64{Int64: userID, Valid: true}
inviteCodeModel.UsedAgentId = sql.NullInt64{Int64: newAgent.Id, Valid: true}
inviteCodeModel.UsedTime = sql.NullTime{Time: time.Now(), Valid: true}
if err := l.svcCtx.AgentInviteCodeModel.UpdateWithVersion(transCtx, session, inviteCodeModel); err != nil {
return errors.Wrapf(err, "更新邀请码状态失败")
}
return nil
})
if transErr != nil {
return nil, transErr
}
// 6. 生成并返回token
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失败: %v", err)
}
now := time.Now().Unix()
return &types.AgentApplyResp{
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
}, nil
}
// findTeamLeader 查找团队首领(钻石代理)
func (l *ApplyForAgentLogic) findTeamLeader(ctx context.Context, agentId int64) (int64, error) {
currentId := agentId
maxDepth := 100
depth := 0
for depth < maxDepth {
builder := l.svcCtx.AgentRelationModel.SelectBuilder().
Where("child_id = ? AND relation_type = ? AND del_state = ?", currentId, 1, 0)
relations, err := l.svcCtx.AgentRelationModel.FindAll(ctx, builder, "")
if err != nil {
return 0, err
}
if len(relations) == 0 {
agent, err := l.svcCtx.AgentModel.FindOne(ctx, currentId)
if err != nil {
return 0, err
}
if agent.Level == 3 {
return agent.Id, nil
}
return 0, nil
}
parentAgent, err := l.svcCtx.AgentModel.FindOne(ctx, relations[0].ParentId)
if err != nil {
return 0, err
}
if parentAgent.Level == 3 {
return parentAgent.Id, nil
}
currentId = parentAgent.Id
depth++
}
return 0, nil
}

View File

@@ -0,0 +1,135 @@
package agent
import (
"context"
"database/sql"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/lzUtils"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type ApplyUpgradeLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewApplyUpgradeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ApplyUpgradeLogic {
return &ApplyUpgradeLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ApplyUpgradeLogic) ApplyUpgrade(req *types.ApplyUpgradeReq) (resp *types.ApplyUpgradeResp, err error) {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
}
// 1. 获取代理信息
agent, 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.DB_ERROR), "查询代理信息失败, %v", err)
}
fromLevel := agent.Level
toLevel := req.ToLevel
// 2. 验证升级条件
if !l.canUpgrade(agent.Level, toLevel, 1) {
return nil, errors.Wrapf(xerr.NewErrMsg("升级条件不满足"), "")
}
// 3. 计算升级费用和返佣
upgradeFee := l.svcCtx.AgentService.GetUpgradeFee(fromLevel, toLevel)
rebateAmount := l.svcCtx.AgentService.GetUpgradeRebate(fromLevel, toLevel)
// 4. 查找原直接上级(用于返佣)
var rebateAgentId int64
parent, err := l.svcCtx.AgentService.FindDirectParent(l.ctx, agent.Id)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(err, "查找直接上级失败")
}
if parent != nil {
rebateAgentId = parent.Id
}
// 5. 使用事务处理升级
var upgradeId int64
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
// 5.1 创建升级记录
upgradeRecord := &model.AgentUpgrade{
AgentId: agent.Id,
FromLevel: fromLevel,
ToLevel: toLevel,
UpgradeType: 1, // 自主付费
UpgradeFee: upgradeFee,
RebateAmount: rebateAmount,
Status: 1, // 待处理
}
if rebateAgentId > 0 {
upgradeRecord.RebateAgentId = sql.NullInt64{Int64: rebateAgentId, Valid: true}
}
upgradeResult, err := l.svcCtx.AgentUpgradeModel.Insert(transCtx, session, upgradeRecord)
if err != nil {
return errors.Wrapf(err, "创建升级记录失败")
}
upgradeId, _ = upgradeResult.LastInsertId()
// 5.2 处理支付(这里假设支付已在外层处理,只记录订单号)
// 实际支付应该在创建升级记录之前完成
// 注意:支付订单号需要从支付回调中获取,这里暂时留空
// 5.3 执行升级操作
if err := l.svcCtx.AgentService.ProcessUpgrade(transCtx, agent.Id, toLevel, 1, upgradeFee, rebateAmount, "", 0); err != nil {
return errors.Wrapf(err, "执行升级操作失败")
}
// 5.4 更新升级记录状态
upgradeRecord.Id = upgradeId
upgradeRecord.Status = 2 // 已完成
upgradeRecord.Remark = lzUtils.StringToNullString("升级成功")
if err := l.svcCtx.AgentUpgradeModel.UpdateWithVersion(transCtx, session, upgradeRecord); err != nil {
return errors.Wrapf(err, "更新升级记录失败")
}
return nil
})
if err != nil {
return nil, err
}
// 返回响应(订单号需要从支付回调中获取,这里暂时返回空)
return &types.ApplyUpgradeResp{
UpgradeId: upgradeId,
OrderNo: "", // 需要从支付回调中获取
}, nil
}
// canUpgrade 检查是否可以升级
func (l *ApplyUpgradeLogic) canUpgrade(fromLevel, toLevel int64, upgradeType int64) bool {
if upgradeType == 1 { // 自主付费
if fromLevel == 1 { // 普通
return toLevel == 2 || toLevel == 3 // 可以升级为黄金或钻石
} else if fromLevel == 2 { // 黄金
return toLevel == 3 // 可以升级为钻石
}
}
return false
}

View File

@@ -0,0 +1,224 @@
package agent
import (
"context"
"fmt"
"time"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/lzUtils"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type ApplyWithdrawalLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewApplyWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ApplyWithdrawalLogic {
return &ApplyWithdrawalLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ApplyWithdrawalLogic) ApplyWithdrawal(req *types.ApplyWithdrawalReq) (resp *types.ApplyWithdrawalResp, err error) {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
}
// 1. 获取代理信息
agent, 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.DB_ERROR), "查询代理信息失败, %v", err)
}
// 2. 验证实名认证
realName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agent.Id)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg("请先完成实名认证"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询实名认证失败, %v", err)
}
// 检查是否已通过三要素核验verify_time不为空表示已通过
if !realName.VerifyTime.Valid {
return nil, errors.Wrapf(xerr.NewErrMsg("请先完成实名认证"), "")
}
// 3. 验证提现金额
if req.Amount <= 0 {
return nil, errors.Wrapf(xerr.NewErrMsg("提现金额必须大于0"), "")
}
// 4. 获取钱包信息
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败, %v", err)
}
// 5. 验证余额
if wallet.Balance < req.Amount {
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("余额不足,当前余额:%.2f", wallet.Balance)), "")
}
// 6. 计算税费
yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
taxInfo, err := l.calculateTax(l.ctx, agent.Id, req.Amount, yearMonth)
if err != nil {
return nil, errors.Wrapf(err, "计算税费失败")
}
// 7. 生成提现单号
withdrawNo := fmt.Sprintf("WD%d%d", time.Now().Unix(), agent.Id)
// 8. 使用事务处理提现申请
var withdrawalId int64
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
// 8.1 冻结余额
wallet.FrozenBalance += req.Amount
wallet.Balance -= req.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
return errors.Wrapf(err, "冻结余额失败")
}
// 8.2 创建提现记录
withdrawal := &model.AgentWithdrawal{
AgentId: agent.Id,
WithdrawNo: withdrawNo,
PayeeAccount: req.PayeeAccount,
PayeeName: req.PayeeName,
Amount: req.Amount,
ActualAmount: taxInfo.ActualAmount,
TaxAmount: taxInfo.TaxAmount,
Status: 1, // 处理中(待审核)
}
withdrawalResult, err := l.svcCtx.AgentWithdrawalModel.Insert(transCtx, session, withdrawal)
if err != nil {
return errors.Wrapf(err, "创建提现记录失败")
}
withdrawalId, _ = withdrawalResult.LastInsertId()
// 8.3 创建扣税记录
taxRecord := &model.AgentWithdrawalTax{
AgentId: agent.Id,
WithdrawalId: withdrawalId,
YearMonth: yearMonth,
WithdrawalAmount: req.Amount,
TaxableAmount: taxInfo.TaxableAmount,
TaxRate: taxInfo.TaxRate,
TaxAmount: taxInfo.TaxAmount,
ActualAmount: taxInfo.ActualAmount,
TaxStatus: 1, // 待扣税
Remark: lzUtils.StringToNullString("提现申请"),
}
if _, err := l.svcCtx.AgentWithdrawalTaxModel.Insert(transCtx, session, taxRecord); err != nil {
return errors.Wrapf(err, "创建扣税记录失败")
}
return nil
})
if err != nil {
return nil, err
}
return &types.ApplyWithdrawalResp{
WithdrawalId: withdrawalId,
WithdrawalNo: withdrawNo,
}, nil
}
// TaxInfo 税费信息
type TaxInfo struct {
TaxableAmount float64 // 应税金额
TaxRate float64 // 税率
TaxAmount float64 // 税费金额
ActualAmount float64 // 实际到账金额
}
// calculateTax 计算税费
func (l *ApplyWithdrawalLogic) calculateTax(ctx context.Context, agentId int64, amount float64, yearMonth int64) (*TaxInfo, error) {
// 获取税率配置默认6%
taxRate := 0.06
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(ctx, "tax_rate")
if err == nil {
if parsedRate, parseErr := l.parseFloat(config.ConfigValue); parseErr == nil {
taxRate = parsedRate
}
}
// 查询本月已提现金额
builder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("agent_id = ? AND year_month = ? AND del_state = ?", agentId, yearMonth, globalkey.DelStateNo)
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(ctx, builder, "")
if err != nil {
return nil, errors.Wrapf(err, "查询月度提现记录失败")
}
// 计算本月累计提现金额
monthlyTotal := 0.0
for _, record := range taxRecords {
monthlyTotal += record.WithdrawalAmount
}
// 获取免税额度配置默认0即无免税额度
exemptionAmount := 0.0
exemptionConfig, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(ctx, "tax_exemption_amount")
if err == nil {
if parsedAmount, parseErr := l.parseFloat(exemptionConfig.ConfigValue); parseErr == nil {
exemptionAmount = parsedAmount
}
}
// 计算应税金额
// 如果本月累计 + 本次提现金额 <= 免税额度,则本次提现免税
// 否则,应税金额 = 本次提现金额 - (免税额度 - 本月累计)(如果免税额度 > 本月累计)
taxableAmount := amount
if exemptionAmount > 0 {
remainingExemption := exemptionAmount - monthlyTotal
if remainingExemption > 0 {
if amount <= remainingExemption {
// 本次提现完全免税
taxableAmount = 0
} else {
// 部分免税
taxableAmount = amount - remainingExemption
}
}
}
// 计算税费
taxAmount := taxableAmount * taxRate
actualAmount := amount - taxAmount
return &TaxInfo{
TaxableAmount: taxableAmount,
TaxRate: taxRate,
TaxAmount: taxAmount,
ActualAmount: actualAmount,
}, nil
}
// parseFloat 解析浮点数
func (l *ApplyWithdrawalLogic) parseFloat(s string) (float64, error) {
var result float64
_, err := fmt.Sscanf(s, "%f", &result)
return result, err
}

View File

@@ -0,0 +1,115 @@
package agent
import (
"context"
"database/sql"
"time"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/tool"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GenerateInviteCodeLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGenerateInviteCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GenerateInviteCodeLogic {
return &GenerateInviteCodeLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GenerateInviteCodeLogic) GenerateInviteCode(req *types.GenerateInviteCodeReq) (resp *types.GenerateInviteCodeResp, err error) {
// 1. 获取当前代理信息
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
}
agent, 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.DB_ERROR), "查询代理信息失败, %v", err)
}
// 2. 验证生成数量
if req.Count <= 0 || req.Count > 100 {
return nil, errors.Wrapf(xerr.NewErrMsg("生成数量必须在1-100之间"), "")
}
// 3. 生成邀请码
codes := make([]string, 0, req.Count)
var expireTime sql.NullTime
if req.ExpireDays > 0 {
expireTime = sql.NullTime{
Time: time.Now().AddDate(0, 0, int(req.ExpireDays)),
Valid: true,
}
}
err = l.svcCtx.AgentInviteCodeModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
for i := int64(0); i < req.Count; i++ {
// 生成8位随机邀请码大小写字母+数字)
var code string
maxRetries := 10 // 最大重试次数
for retry := 0; retry < maxRetries; retry++ {
code = tool.Krand(8, tool.KC_RAND_KIND_ALL)
// 检查邀请码是否已存在
_, err := l.svcCtx.AgentInviteCodeModel.FindOneByCode(transCtx, code)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 邀请码不存在,可以使用
break
}
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "检查邀请码失败, %v", err)
}
// 邀请码已存在,继续生成
if retry == maxRetries-1 {
return errors.Wrapf(xerr.NewErrMsg("生成邀请码失败,请重试"), "")
}
}
// 创建邀请码记录
inviteCode := &model.AgentInviteCode{
Code: code,
AgentId: sql.NullInt64{Int64: agent.Id, Valid: true},
TargetLevel: 1, // 代理发放的邀请码,目标等级为普通代理
Status: 0, // 未使用
ExpireTime: expireTime,
Remark: sql.NullString{String: req.Remark, Valid: req.Remark != ""},
}
_, err := l.svcCtx.AgentInviteCodeModel.Insert(transCtx, session, inviteCode)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建邀请码失败, %v", err)
}
codes = append(codes, code)
}
return nil
})
if err != nil {
return nil, err
}
return &types.GenerateInviteCodeResp{
Codes: codes,
}, nil
}

View File

@@ -0,0 +1,158 @@
package agent
import (
"context"
"encoding/hex"
"encoding/json"
"strconv"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-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)
}
// 1. 获取代理信息
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.DB_ERROR), "查询代理信息失败, %v", err)
}
// 2. 获取系统配置
basePrice, err := l.getConfigFloat("base_price")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取基础底价配置失败, %v", err)
}
systemMaxPrice, err := l.getConfigFloat("system_max_price")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取系统价格上限配置失败, %v", err)
}
// 4. 计算实际底价(基础底价 + 等级加成)
levelBonus := l.getLevelBonus(agentModel.Level)
actualBasePrice := basePrice + float64(levelBonus)
// 5. 验证设定价格范围
if req.SetPrice < actualBasePrice || req.SetPrice > systemMaxPrice {
return nil, errors.Wrapf(xerr.NewErrMsg("设定价格必须在 %.2f 到 %.2f 之间"), "设定价格必须在 %.2f 到 %.2f 之间", actualBasePrice, systemMaxPrice)
}
// 6. 检查是否已存在相同的链接(同一代理、同一产品、同一价格)
builder := l.svcCtx.AgentLinkModel.SelectBuilder().Where(squirrel.And{
squirrel.Eq{"agent_id": agentModel.Id},
squirrel.Eq{"product_id": req.ProductId},
squirrel.Eq{"set_price": req.SetPrice},
squirrel.Eq{"del_state": globalkey.DelStateNo},
})
existingLinks, err := l.svcCtx.AgentLinkModel.FindAll(l.ctx, builder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询推广链接失败, %v", err)
}
if len(existingLinks) > 0 {
// 已存在,直接返回
return &types.AgentGeneratingLinkResp{
LinkIdentifier: existingLinks[0].LinkIdentifier,
}, nil
}
// 7. 生成推广链接标识
var agentIdentifier types.AgentIdentifier
agentIdentifier.AgentID = agentModel.Id
agentIdentifier.ProductID = req.ProductId
agentIdentifier.SetPrice = req.SetPrice
agentIdentifierByte, err := json.Marshal(agentIdentifier)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "序列化标识失败, %v", err)
}
// 8. 加密链接标识
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", decodeErr)
}
encrypted, err := crypto.AesEncryptURL(agentIdentifierByte, key)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密链接标识失败, %v", err)
}
// 9. 保存推广链接
agentLink := &model.AgentLink{
AgentId: agentModel.Id,
UserId: userID,
ProductId: req.ProductId,
LinkIdentifier: encrypted,
SetPrice: req.SetPrice,
ActualBasePrice: actualBasePrice,
}
_, 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
}
// getLevelBonus 获取等级加成
func (l *GeneratingLinkLogic) getLevelBonus(level int64) int64 {
switch level {
case 1: // 普通
return 6
case 2: // 黄金
return 3
case 3: // 钻石
return 0
default:
return 0
}
}
// getConfigFloat 获取配置值(浮点数)
func (l *GeneratingLinkLogic) getConfigFloat(configKey string) (float64, error) {
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, configKey)
if err != nil {
return 0, err
}
value, err := strconv.ParseFloat(config.ConfigValue, 64)
if err != nil {
return 0, errors.Wrapf(err, "解析配置值失败, key: %s, value: %s", configKey, config.ConfigValue)
}
return value, nil
}

View File

@@ -0,0 +1,114 @@
package agent
import (
"context"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-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) {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
}
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 不是代理,返回空信息
return &types.AgentInfoResp{
AgentId: 0,
Level: 0,
LevelName: "",
Region: "",
Mobile: "",
WechatId: "",
TeamLeaderId: 0,
IsRealName: false,
}, nil
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
}
// 解密手机号
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.DB_ERROR), "查询实名认证失败, %v", err)
}
if agentRealName != nil && agentRealName.VerifyTime.Valid { // verify_time不为空表示已通过三要素核验
isRealName = true
}
// 获取微信号
wechatId := ""
if agent.WechatId.Valid {
wechatId = agent.WechatId.String
}
// 获取团队首领ID
teamLeaderId := int64(0)
if agent.TeamLeaderId.Valid {
teamLeaderId = agent.TeamLeaderId.Int64
}
// 获取区域
region := ""
if agent.Region.Valid {
region = agent.Region.String
}
return &types.AgentInfoResp{
AgentId: agent.Id,
Level: agent.Level,
LevelName: l.getLevelName(agent.Level),
Region: region,
Mobile: mobile,
WechatId: wechatId,
TeamLeaderId: teamLeaderId,
IsRealName: isRealName,
}, nil
}
// getLevelName 获取等级名称
func (l *GetAgentInfoLogic) getLevelName(level int64) string {
switch level {
case 1:
return "普通"
case 2:
return "黄金"
case 3:
return "钻石"
default:
return ""
}
}

View File

@@ -0,0 +1,147 @@
package agent
import (
"context"
"strconv"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-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,
}
}
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)
}
// 1. 获取代理信息
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.DB_ERROR), "查询代理信息失败, %v", err)
}
// 2. 获取系统配置
basePrice, err := l.getConfigFloat("base_price")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取基础底价配置失败, %v", err)
}
systemMaxPrice, err := l.getConfigFloat("system_max_price")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取系统价格上限配置失败, %v", err)
}
priceThreshold, err := l.getConfigFloat("price_threshold")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取提价标准阈值配置失败, %v", err)
}
priceFeeRate, err := l.getConfigFloat("price_fee_rate")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取提价手续费比例配置失败, %v", err)
}
// 3. 计算等级加成
levelBonus := l.getLevelBonus(agentModel.Level)
// 4. 查询所有产品配置
builder := l.svcCtx.AgentProductConfigModel.SelectBuilder()
productConfigs, err := l.svcCtx.AgentProductConfigModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询产品配置失败, %v", err)
}
// 5. 组装响应数据
var respList []types.ProductConfigItem
for _, productConfig := range productConfigs {
// 使用产品配置中的基础底价,如果没有则使用系统配置的基础底价
productBasePrice := basePrice
if productConfig.BasePrice > 0 {
productBasePrice = productConfig.BasePrice
}
// 计算该产品的实际底价
productActualBasePrice := productBasePrice + float64(levelBonus)
// 价格范围:实际底价 ≤ 设定价格 ≤ 系统价格上限(或产品配置的最高价格)
priceRangeMin := productActualBasePrice
priceRangeMax := systemMaxPrice
if productConfig.SystemMaxPrice > 0 && productConfig.SystemMaxPrice < systemMaxPrice {
priceRangeMax = productConfig.SystemMaxPrice
}
// 使用产品配置的提价阈值和手续费比例,如果没有则使用系统配置
productPriceThreshold := priceThreshold
if productConfig.PriceThreshold.Valid && productConfig.PriceThreshold.Float64 > 0 {
productPriceThreshold = productConfig.PriceThreshold.Float64
}
productPriceFeeRate := priceFeeRate
if productConfig.PriceFeeRate.Valid && productConfig.PriceFeeRate.Float64 > 0 {
productPriceFeeRate = productConfig.PriceFeeRate.Float64
}
respList = append(respList, types.ProductConfigItem{
ProductId: productConfig.ProductId,
ProductName: productConfig.ProductName,
BasePrice: productBasePrice,
LevelBonus: float64(levelBonus),
ActualBasePrice: productActualBasePrice,
PriceRangeMin: priceRangeMin,
PriceRangeMax: priceRangeMax,
PriceThreshold: productPriceThreshold,
PriceFeeRate: productPriceFeeRate,
})
}
return &types.AgentProductConfigResp{
List: respList,
}, nil
}
// getLevelBonus 获取等级加成
func (l *GetAgentProductConfigLogic) getLevelBonus(level int64) int64 {
switch level {
case 1: // 普通
return 6
case 2: // 黄金
return 3
case 3: // 钻石
return 0
default:
return 0
}
}
// getConfigFloat 获取配置值(浮点数)
func (l *GetAgentProductConfigLogic) getConfigFloat(configKey string) (float64, error) {
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, configKey)
if err != nil {
return 0, err
}
value, err := strconv.ParseFloat(config.ConfigValue, 64)
if err != nil {
return 0, errors.Wrapf(err, "解析配置值失败, key: %s, value: %s", configKey, config.ConfigValue)
}
return value, nil
}

View File

@@ -0,0 +1,102 @@
package agent
import (
"context"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetCommissionListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetCommissionListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCommissionListLogic {
return &GetCommissionListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetCommissionListLogic) GetCommissionList(req *types.GetCommissionListReq) (resp *types.GetCommissionListResp, err error) {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
}
// 1. 获取代理信息
agent, 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.DB_ERROR), "查询代理信息失败, %v", err)
}
// 2. 构建查询条件
builder := l.svcCtx.AgentCommissionModel.SelectBuilder().
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo).
OrderBy("create_time DESC")
// 3. 分页查询
page := req.Page
if page <= 0 {
page = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
offset := (page - 1) * pageSize
// 4. 查询总数
total, err := l.svcCtx.AgentCommissionModel.FindCount(l.ctx, builder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询佣金总数失败, %v", err)
}
// 5. 查询列表
builder = builder.Limit(uint64(pageSize)).Offset(uint64(offset))
commissions, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询佣金列表失败, %v", err)
}
// 6. 组装响应
var list []types.CommissionItem
for _, commission := range commissions {
// 查询产品名称
productName := ""
if commission.ProductId > 0 {
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, commission.ProductId)
if err == nil {
productName = product.ProductName
}
}
list = append(list, types.CommissionItem{
Id: commission.Id,
OrderId: commission.OrderId,
ProductName: productName,
Amount: commission.Amount,
Status: commission.Status,
CreateTime: commission.CreateTime.Format("2006-01-02 15:04:05"),
})
}
return &types.GetCommissionListResp{
Total: total,
List: list,
}, nil
}

View File

@@ -0,0 +1,89 @@
package agent
import (
"context"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetInviteCodeListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetInviteCodeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetInviteCodeListLogic {
return &GetInviteCodeListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetInviteCodeListLogic) GetInviteCodeList(req *types.GetInviteCodeListReq) (resp *types.GetInviteCodeListResp, err error) {
// 1. 获取当前代理信息
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
}
agent, 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.DB_ERROR), "查询代理信息失败, %v", err)
}
// 2. 构建查询条件
builder := l.svcCtx.AgentInviteCodeModel.SelectBuilder().
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo)
if req.Status >= 0 {
builder = builder.Where("status = ?", req.Status)
}
// 3. 分页查询
list, total, err := l.svcCtx.AgentInviteCodeModel.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)
}
// 4. 格式化返回数据
items := make([]types.InviteCodeItem, 0, len(list))
for _, v := range list {
item := types.InviteCodeItem{
Id: v.Id,
Code: v.Code,
TargetLevel: v.TargetLevel,
Status: v.Status,
CreateTime: v.CreateTime.Format("2006-01-02 15:04:05"),
}
if v.UsedTime.Valid {
item.UsedTime = v.UsedTime.Time.Format("2006-01-02 15:04:05")
}
if v.ExpireTime.Valid {
item.ExpireTime = v.ExpireTime.Time.Format("2006-01-02 15:04:05")
}
if v.Remark.Valid {
item.Remark = v.Remark.String
}
items = append(items, item)
}
return &types.GetInviteCodeListResp{
Total: total,
List: items,
}, nil
}

Some files were not shown because too many files have changed in this diff Show More