first commit

This commit is contained in:
2026-03-18 00:01:48 +08:00
commit a0c623fd81
425 changed files with 42318 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
package admin_api
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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 == "" {
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: %s", 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,79 @@
package admin_api
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/google/uuid"
"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{
Id: uuid.NewString(),
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. 返回结果
_ = result
return &types.AdminCreateApiResp{Id: apiData.Id}, nil
}

View File

@@ -0,0 +1,68 @@
package admin_api
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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 == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API ID不能为空, id: %s", 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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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 == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API ID不能为空, id: %s", 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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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 == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"API ID不能为空, id: %s", 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,96 @@
package admin_auth
import (
"context"
"os"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
jwtx "in-server/common/jwt"
"in-server/common/xerr"
"in-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 os.Getenv("ENV") != "development" {
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([]string, 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: "",
Platform: model.PlatformAdmin,
UserType: model.UserTypeAdmin,
IsAgent: 0,
}
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,94 @@
package admin_feature
import (
"context"
"encoding/hex"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/google/uuid"
)
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{
Id: uuid.NewString(),
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,48 @@
package admin_feature
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/google/uuid"
"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{
Id: uuid.NewString(),
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. 返回结果
_ = result
return &types.AdminCreateFeatureResp{Id: data.Id}, nil
}

View File

@@ -0,0 +1,45 @@
package admin_feature
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"in-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: "",
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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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 == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"功能ID不能为空, id: %s", 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,99 @@
package admin_menu
import (
"context"
"database/sql"
"encoding/json"
"time"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/google/uuid"
)
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 != "" {
parentMenu, err := l.svcCtx.AdminMenuModel.FindOne(l.ctx, req.Pid)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询父菜单失败, id: %s, err: %v", req.Pid, err)
}
if parentMenu == nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "父菜单不存在, id: %s", 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{
Id: uuid.NewString(),
Pid: sql.NullString{String: req.Pid, Valid: 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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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 == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"菜单ID不能为空, id: %s", 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: %s", 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,251 @@
package admin_menu
import (
"context"
"database/sql"
"sort"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/ctxdata"
"in-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并发获取用户角色UUID 字符串)
var roleIds []string
var permissions []*struct {
RoleId string
}
type UserRoleResult struct {
RoleId string
}
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 string }{RoleId: item.RoleId})
}
},
)
if err != nil {
return nil, err
}
for _, permission := range permissions {
roleIds = append(roleIds, permission.RoleId)
}
// 使用MapReduceVoid并发获取角色菜单UUID 字符串)
var menuIds []string
var roleMenus []*struct {
MenuId string
}
type RoleMenuResult struct {
MenuId string
}
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 string }{MenuId: item.MenuId})
}
},
)
if err != nil {
return nil, err
}
for _, roleMenu := range roleMenus {
menuIds = append(menuIds, roleMenu.MenuId)
}
// 使用MapReduceVoid并发获取菜单
type AdminMenuStruct struct {
Id string
Pid sql.NullString
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 ""
}()
menuMap[menu.Id] = 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) string {
if item.Pid.Valid {
return item.Pid.String
}
return "0"
})
// 递归构建菜单树(字符串键)
var buildMenuTree func(parentId string) []types.GetMenuAllResp
buildMenuTree = func(parentId string) []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 {
if menu, exists := menuMap[childMenu.Id]; 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"
"in-server/app/main/api/internal/svc"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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[string]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.String,
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.Valid && menu.Pid.String != "0" {
// 找到父菜单
if parent, exists := menuMap[menu.Pid.String]; exists {
// 添加当前菜单到父菜单的子菜单列表
children := append(parent.Children, menuMap[menu.Id])
parent.Children = children
menuMap[menu.Pid.String] = parent
}
}
}
// 提取顶级菜单ParentId为0到响应列表
result := make([]types.MenuListItem, 0)
for _, menu := range menus {
if menu.Pid.Valid && menu.Pid.String == "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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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 != nil && *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: %s, err: %v", *req.Pid, err)
}
if parentMenu == nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "父菜单不存在, id: %s", *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 = sql.NullString{String: *req.Pid, Valid: req.Pid != nil}
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,52 @@
package admin_notification
import (
"context"
"database/sql"
"time"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/google/uuid"
"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{
Id: uuid.NewString(),
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)
}
_ = result
return &types.AdminCreateNotificationResp{Id: data.Id}, nil
}

View File

@@ -0,0 +1,38 @@
package admin_notification
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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,30 @@
package admin_order
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
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) {
// todo: add your logic here and delete this line
return
}

View File

@@ -0,0 +1,30 @@
package admin_order
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
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) {
// todo: add your logic here and delete this line
return
}

View File

@@ -0,0 +1,99 @@
package admin_order
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/globalkey"
"in-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 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"),
QueryState: queryState,
// 代理系统已下线,以下字段保留兼容但不再使用
IsAgentOrder: false,
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,227 @@
package admin_order
import (
"context"
"sync"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/globalkey"
"in-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.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[string]string)
queryStateMap := make(map[string]string)
var mu sync.Mutex
// 批量获取查询状态
if len(orders) > 0 {
orderIds := make([]string, 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[string]bool)
for _, query := range queries {
queryStateMap[query.OrderId] = query.QueryState
foundOrderIds[query.OrderId] = true
}
// 3. 查找未找到查询状态的订单是否在清理日志中
notFoundOrderIds := make([]string, 0)
for _, orderId := range orderIds {
if !foundOrderIds[orderId] {
notFoundOrderIds = append(notFoundOrderIds, orderId)
}
}
if len(notFoundOrderIds) > 0 {
// 查询清理日志
cleanupBuilder := l.svcCtx.QueryCleanupDetailModel.SelectBuilder().
Where(squirrel.Eq{"order_id": notFoundOrderIds}).
Where("del_state = ?", globalkey.DelStateNo).
OrderBy("create_time DESC")
cleanupDetails, err := l.svcCtx.QueryCleanupDetailModel.FindAll(l.ctx, cleanupBuilder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 查询清理日志失败 err: %v", err)
}
// 记录已清理的订单状态
for _, detail := range cleanupDetails {
if _, exists := queryStateMap[detail.OrderId]; !exists {
queryStateMap[detail.OrderId] = model.QueryStateCleaned // 使用常量标记为已清除状态
}
}
// 对于既没有查询状态也没有清理记录的订单,不设置状态(保持为空字符串)
for _, orderId := range notFoundOrderIds {
if _, exists := queryStateMap[orderId]; !exists {
queryStateMap[orderId] = "" // 未知状态保持为空字符串
}
}
}
}
// 并发获取产品信息
err = mr.MapReduceVoid(func(source chan<- interface{}) {
for _, order := range orders {
source <- order
}
}, func(item interface{}, writer mr.Writer[struct{}], cancel func(error)) {
order := item.(*model.Order)
// 获取产品信息
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, order.ProductId)
if err != nil && !errors.Is(err, model.ErrNotFound) {
cancel(errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 查询产品信息失败 err: %v", err))
return
}
mu.Lock()
if product != nil {
productMap[product.Id] = product.ProductName
} else {
productMap[order.ProductId] = "" // 产品不存在时设置为空字符串
}
mu.Unlock()
writer.Write(struct{}{})
}, func(pipe <-chan struct{}, cancel func(error)) {
for range pipe {
}
})
if err != nil {
return nil, err
}
// 构建响应
resp = &types.AdminGetOrderListResp{
Total: total,
Items: make([]types.OrderListItem, 0, len(orders)),
}
for _, order := range orders {
item := types.OrderListItem{
Id: order.Id,
OrderNo: order.OrderNo,
PlatformOrderId: order.PlatformOrderId.String,
ProductName: productMap[order.ProductId],
PaymentPlatform: order.PaymentPlatform,
PaymentScene: order.PaymentScene,
Amount: order.Amount,
Status: order.Status,
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
QueryState: queryStateMap[order.Id],
}
if order.PayTime.Valid {
item.PayTime = order.PayTime.Time.Format("2006-01-02 15:04:05")
}
if order.RefundTime.Valid {
item.RefundTime = order.RefundTime.Time.Format("2006-01-02 15:04:05")
}
// 代理系统已下线,相关字段保留兼容但不再使用
item.IsAgentOrder = false
item.AgentProcessStatus = ""
resp.Items = append(resp.Items, item)
}
return resp, nil
}

View File

@@ -0,0 +1,30 @@
package admin_order
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
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) {
// todo: add your logic here and delete this line
return
}

View File

@@ -0,0 +1,30 @@
package admin_order
import (
"context"
"in-server/app/main/api/internal/svc"
"in-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) {
// todo: add your logic here and delete this line
return
}

View File

@@ -0,0 +1,30 @@
package admin_order
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
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) {
// todo: add your logic here and delete this line
return
}

View File

@@ -0,0 +1,73 @@
package admin_platform_user
import (
"context"
"database/sql"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"github.com/google/uuid"
"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) {
// 加密手机号
key := l.svcCtx.Config.Encrypt.SecretKey
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, key)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建平台用户, 加密手机号失败: %v", err)
}
// 校验手机号唯一性(按加密后值)
_, err = l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
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)
}
// 处理密码(如提供则加密存储)
var password sql.NullString
if req.Password != "" {
hashedPassword, hashErr := crypto.PasswordHash(req.Password)
if hashErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建平台用户, 加密密码失败: %v", hashErr)
}
password = sql.NullString{String: hashedPassword, Valid: true}
}
user := &model.User{
Id: uuid.NewString(),
Mobile: sql.NullString{String: encryptedMobile, Valid: true},
Password: password,
Nickname: sql.NullString{String: req.Nickname, Valid: req.Nickname != ""},
Info: req.Info,
Inside: req.Inside,
}
_, err = l.svcCtx.UserModel.Insert(l.ctx, nil, user)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户失败: %v", err)
}
resp = &types.AdminCreatePlatformUserResp{Id: user.Id}
return resp, nil
}

View File

@@ -0,0 +1,43 @@
package admin_platform_user
import (
"context"
"time"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/xerr"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/xerr"
"in-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,71 @@
package admin_platform_user
import (
"context"
"database/sql"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/xerr"
"in-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), "用户不存在: %s, 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 {
// 密码为空字符串时不修改密码,避免误清空
if *req.Password != "" {
hashedPassword, hashErr := crypto.PasswordHash(*req.Password)
if hashErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "更新用户密码失败: %v", hashErr)
}
user.Password = sql.NullString{String: hashedPassword, Valid: true}
}
}
_, 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,61 @@
package admin_product
import (
"context"
"database/sql"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
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{
Id: uuid.NewString(),
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. 数据库操作(仅创建产品本身)
var productId string
err = l.svcCtx.ProductModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
_, err := l.svcCtx.ProductModel.Insert(ctx, session, data)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"创建产品失败, err: %v, req: %+v", err, req)
}
productId = data.Id
return nil
})
if err != nil {
return nil, err
}
// 3. 返回结果
return &types.AdminCreateProductResp{Id: productId}, nil
}

View File

@@ -0,0 +1,54 @@
package admin_product
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
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.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 2.1 软删除产品
err = l.svcCtx.ProductModel.DeleteSoft(ctx, session, record)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"删除产品失败, err: %v, id: %d", err, req.Id)
}
return nil
})
if err != nil {
return nil, err
}
// 3. 返回结果
return &types.AdminDeleteProductResp{Success: true}, nil
}

View File

@@ -0,0 +1,49 @@
package admin_product
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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([]string, 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 string
}{i, id}
}
}, func(item interface{}, writer mr.Writer[featureResult], cancel func(error)) {
data := item.(struct {
index int
id string
})
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: %s, 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[string]*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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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,161 @@
package admin_product
import (
"context"
"sync"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mr"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/google/uuid"
)
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[string]*model.ProductFeature)
for _, item := range existingList {
existingMap[item.FeatureId] = item
}
// 3. 构建新关联的映射
newMap := make(map[string]*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 []string
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.(string)
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 string
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 string
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{
Id: uuid.NewString(),
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,75 @@
package admin_product
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/xerr"
"database/sql"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
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.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 3.1 更新产品
_, err = l.svcCtx.ProductModel.Update(ctx, session, record)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"更新产品失败, err: %v, req: %+v", err, req)
}
return nil
})
if err != nil {
return nil, err
}
// 4. 返回结果
return &types.AdminUpdateProductResp{Success: true}, nil
}

View File

@@ -0,0 +1,62 @@
package admin_query
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/globalkey"
"in-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"
"sync"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/globalkey"
"in-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mr"
)
type AdminGetQueryCleanupDetailListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetQueryCleanupDetailListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetQueryCleanupDetailListLogic {
return &AdminGetQueryCleanupDetailListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetQueryCleanupDetailListLogic) AdminGetQueryCleanupDetailList(req *types.AdminGetQueryCleanupDetailListReq) (resp *types.AdminGetQueryCleanupDetailListResp, err error) {
// 1. 验证清理日志是否存在
_, err = l.svcCtx.QueryCleanupLogModel.FindOne(l.ctx, req.LogId)
if err != nil {
if err == model.ErrNotFound {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "清理日志不存在, log_id: %d", req.LogId)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询清理日志失败, log_id: %d, err: %v", req.LogId, err)
}
// 2. 构建查询条件
builder := l.svcCtx.QueryCleanupDetailModel.SelectBuilder().
Where("cleanup_log_id = ?", req.LogId).
Where("del_state = ?", globalkey.DelStateNo)
// 3. 并发获取总数和列表
var total int64
var details []*model.QueryCleanupDetail
err = mr.Finish(func() error {
var err error
total, err = l.svcCtx.QueryCleanupDetailModel.FindCount(l.ctx, builder, "id")
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询清理详情总数失败 err: %v", err)
}
return nil
}, func() error {
var err error
details, err = l.svcCtx.QueryCleanupDetailModel.FindPageListByPage(l.ctx, builder, req.Page, req.PageSize, "id DESC")
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询清理详情列表失败 err: %v", err)
}
return nil
})
if err != nil {
return nil, err
}
// 4. 获取所有产品ID
productIds := make([]string, 0, len(details))
for _, detail := range details {
productIds = append(productIds, detail.ProductId)
}
// 5. 并发获取产品信息
productMap := make(map[string]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.(string)
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: %s, 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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/globalkey"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"in-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 string, 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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/google/uuid"
)
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{
Id: uuid.NewString(),
RoleName: req.RoleName,
RoleCode: req.RoleCode,
Description: req.Description,
Status: req.Status,
Sort: req.Sort,
}
var roleId string
// 使用事务创建角色和关联菜单
err = l.svcCtx.AdminRoleModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 创建角色
_, err := l.svcCtx.AdminRoleModel.Insert(ctx, session, role)
if err != nil {
return errors.New("插入新角色失败")
}
roleId = role.Id
// 创建角色菜单关联
if len(req.MenuIds) > 0 {
for _, menuId := range req.MenuIds {
roleMenu := &model.AdminRoleMenu{
Id: uuid.NewString(),
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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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 []string
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) string {
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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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[[]string], 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) string {
return item.MenuId
})
writer.Write(menuIds)
}, func(pipe <-chan []string, 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,150 @@
package admin_role
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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"
"github.com/google/uuid"
)
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[string]*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: %s", menuId)
}
}
// 4. 找出需要删除和新增的关联
var toDelete []*model.AdminRoleMenu
var toInsert []string
// 需要删除的:当前存在但新列表中没有的
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{
Id: uuid.NewString(),
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,98 @@
package admin_role_api
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/google/uuid"
)
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 == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"角色ID不能为空, roleId: %s", 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 == "" {
continue
}
// 检查API是否存在
_, err := l.svcCtx.AdminApiModel.FindOne(l.ctx, apiId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
logx.Errorf("API不存在, apiId: %s", apiId)
continue
}
logx.Errorf("查询API失败, err: %v, apiId: %s", 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{
Id: uuid.NewString(),
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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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: api.Id,
RoleId: "",
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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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 == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"角色ID不能为空, roleId: %s", 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: %s", 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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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 == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"角色ID不能为空, roleId: %s", 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 == "" {
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: %s, apiId: %s", 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,98 @@
package admin_role_api
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/google/uuid"
)
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 == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.PARAM_VERIFICATION_ERROR),
"角色ID不能为空, roleId: %s", 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: %s", req.RoleId)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
"查询角色失败, err: %v, roleId: %s", 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 == "" {
continue
}
// 检查API是否存在
_, err := l.svcCtx.AdminApiModel.FindOne(l.ctx, apiId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
logx.Errorf("API不存在, apiId: %s", apiId)
continue
}
logx.Errorf("查询API失败, err: %v, apiId: %s", err, apiId)
continue
}
// 创建新的关联
roleApiData := &model.AdminRoleApi{
Id: uuid.NewString(),
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,86 @@
package admin_user
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"github.com/google/uuid"
"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{
Id: uuid.NewString(),
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 {
// 创建用户
_, err := l.svcCtx.AdminUserModel.Insert(ctx, session, user)
if err != nil {
return err
}
// 创建用户角色关联
if len(req.RoleIds) > 0 {
for _, roleId := range req.RoleIds {
userRole := &model.AdminUserRole{
Id: uuid.NewString(),
UserId: user.Id,
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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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 []string
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) string {
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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-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[[]string], 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) string {
return item.RoleId
})
writer.Write(roleIds)
}, func(pipe <-chan []string, 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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"in-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,143 @@
package admin_user
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-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"
"github.com/google/uuid"
)
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[string]*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: %s", roleId)
}
}
// 4. 找出需要删除和新增的关联
var toDelete []*model.AdminUserRole
var toInsert []string
// 需要删除的:当前存在但新列表中没有的
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{
Id: uuid.NewString(),
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"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/ctxdata"
"in-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([]string, 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,43 @@
package app
import (
"context"
"strconv"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"github.com/zeromicro/go-zero/core/logx"
)
type GetAppConfigLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetAppConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAppConfigLogic {
return &GetAppConfigLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetAppConfigLogic) GetAppConfig() (resp *types.GetAppConfigResp, err error) {
retentionDays := int64(0)
cfg, cfgErr := l.svcCtx.QueryCleanupConfigModel.FindOneByConfigKey(l.ctx, "retention_days")
if cfgErr == nil && cfg.Status == 1 {
if v, parseErr := strconv.ParseInt(cfg.ConfigValue, 10, 64); parseErr == nil && v >= 0 {
retentionDays = v
}
} else if cfgErr != nil && cfgErr != model.ErrNotFound {
l.Errorf("获取清理配置失败: %v", cfgErr)
}
return &types.GetAppConfigResp{
QueryRetentionDays: retentionDays,
WechatH5LoginEnabled: l.svcCtx.Config.WechatH5.Enabled,
}, nil
}

View File

@@ -0,0 +1,31 @@
package app
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetAppVersionLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetAppVersionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAppVersionLogic {
return &GetAppVersionLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetAppVersionLogic) GetAppVersion() (resp *types.GetAppVersionResp, err error) {
return &types.GetAppVersionResp{
Version: "1.0.0",
WgtUrl: "https://www.rongcc.com/app_version/in_1.0.0.wgt",
}, nil
}

View File

@@ -0,0 +1,31 @@
package app
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type HealthCheckLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewHealthCheckLogic(ctx context.Context, svcCtx *svc.ServiceContext) *HealthCheckLogic {
return &HealthCheckLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *HealthCheckLogic) HealthCheck() (resp *types.HealthCheckResp, err error) {
return &types.HealthCheckResp{
Status: "UP",
Message: "Service is healthy HahaHa",
}, nil
}

View File

@@ -0,0 +1,105 @@
package auth
import (
"context"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"fmt"
"math/rand"
"time"
"github.com/pkg/errors"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
dysmsapi "github.com/alibabacloud-go/dysmsapi-20170525/v3/client"
"github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
"github.com/zeromicro/go-zero/core/logx"
)
type SendSmsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLogic {
return &SendSmsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
secretKey := l.svcCtx.Config.Encrypt.SecretKey
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 加密手机号失败: %v", err)
}
// 检查手机号是否在一分钟内已发送过验证码
limitCodeKey := fmt.Sprintf("limit:%s:%s", req.ActionType, encryptedMobile)
exists, err := l.svcCtx.Redis.Exists(limitCodeKey)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 读取redis缓存失败: %s", encryptedMobile)
}
if exists {
// 如果 Redis 中已经存在标记,说明在 1 分钟内请求过,返回错误
return errors.Wrapf(xerr.NewErrMsg("一分钟内不能重复发送验证码"), "短信发送, 手机号1分钟内重复请求发送验证码: %s", encryptedMobile)
}
code := fmt.Sprintf("%06d", rand.New(rand.NewSource(time.Now().UnixNano())).Intn(1000000))
// 发送短信
smsResp, err := l.sendSmsRequest(req.Mobile, code)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 调用阿里客户端失败: %v", err)
}
if *smsResp.Body.Code != "OK" {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 阿里客户端响应失败: %s", *smsResp.Body.Message)
}
codeKey := fmt.Sprintf("%s:%s", req.ActionType, encryptedMobile)
// 将验证码保存到 Redis设置过期时间
err = l.svcCtx.Redis.Setex(codeKey, code, l.svcCtx.Config.VerifyCode.ValidTime) // 验证码有效期5分钟
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 验证码设置过期时间失败: %v", err)
}
// 在 Redis 中设置 1 分钟的标记,限制重复请求
err = l.svcCtx.Redis.Setex(limitCodeKey, code, 60) // 标记 1 分钟内不能重复请求
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 验证码设置限制重复请求失败: %v", err)
}
return nil
}
// CreateClient 创建阿里云短信客户端
func (l *SendSmsLogic) CreateClient() (*dysmsapi.Client, error) {
config := &openapi.Config{
AccessKeyId: &l.svcCtx.Config.VerifyCode.AccessKeyID,
AccessKeySecret: &l.svcCtx.Config.VerifyCode.AccessKeySecret,
}
config.Endpoint = tea.String(l.svcCtx.Config.VerifyCode.EndpointURL)
return dysmsapi.NewClient(config)
}
// sendSmsRequest 发送短信请求
func (l *SendSmsLogic) sendSmsRequest(mobile, code string) (*dysmsapi.SendSmsResponse, error) {
// 初始化阿里云短信客户端
cli, err := l.CreateClient()
if err != nil {
return nil, err
}
request := &dysmsapi.SendSmsRequest{
SignName: tea.String(l.svcCtx.Config.VerifyCode.SignName),
TemplateCode: tea.String(l.svcCtx.Config.VerifyCode.TemplateCode),
PhoneNumbers: tea.String(mobile),
TemplateParam: tea.String(fmt.Sprintf("{\"code\":\"%s\"}", code)),
}
runtime := &service.RuntimeOptions{}
return cli.SendSmsWithOptions(request, runtime)
}

View File

@@ -0,0 +1,51 @@
package authorization
import (
"context"
"errors"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type DownloadAuthorizationDocumentLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDownloadAuthorizationDocumentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DownloadAuthorizationDocumentLogic {
return &DownloadAuthorizationDocumentLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DownloadAuthorizationDocumentLogic) DownloadAuthorizationDocument(req *types.DownloadAuthorizationDocumentReq) (resp *types.DownloadAuthorizationDocumentResp, err error) {
// 1. 从数据库获取授权书信息
authDoc, err := l.svcCtx.AuthorizationDocumentModel.FindOne(l.ctx, req.DocumentId)
if err != nil {
logx.Errorf("获取授权书失败: documentId=%s, error=%v", req.DocumentId, err)
return nil, err
}
// 2. 检查授权书状态
if authDoc.Status != "active" {
logx.Errorf("授权书状态异常: documentId=%s, status=%s", req.DocumentId, authDoc.Status)
return nil, errors.New("授权书不可用")
}
// 3. 构建完整文件URL
fullFileURL := l.svcCtx.AuthorizationService.GetFullFileURL(authDoc.FileUrl)
// 4. 构建响应
resp = &types.DownloadAuthorizationDocumentResp{
FileName: authDoc.FileName,
FileUrl: fullFileURL,
}
return resp, nil
}

View File

@@ -0,0 +1,62 @@
package authorization
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetAuthorizationDocumentByOrderLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetAuthorizationDocumentByOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAuthorizationDocumentByOrderLogic {
return &GetAuthorizationDocumentByOrderLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetAuthorizationDocumentByOrderLogic) GetAuthorizationDocumentByOrder(req *types.GetAuthorizationDocumentByOrderReq) (resp *types.GetAuthorizationDocumentByOrderResp, err error) {
// 1. 根据订单ID查询授权书列表
authDocs, err := l.svcCtx.AuthorizationDocumentModel.FindByOrderId(l.ctx, req.OrderId)
if err != nil {
logx.Errorf("根据订单ID获取授权书失败: orderId=%d, error=%v", req.OrderId, err)
return nil, err
}
// 2. 构建响应列表
var documents []types.AuthorizationDocumentInfo
for _, authDoc := range authDocs {
// 只返回状态为active的授权书
if authDoc.Status == "active" {
fullFileURL := l.svcCtx.AuthorizationService.GetFullFileURL(authDoc.FileUrl)
documents = append(documents, types.AuthorizationDocumentInfo{
DocumentId: authDoc.Id,
UserId: authDoc.UserId,
OrderId: authDoc.OrderId,
QueryId: authDoc.QueryId,
FileName: authDoc.FileName,
FileUrl: fullFileURL,
FileSize: authDoc.FileSize,
FileType: authDoc.FileType,
Status: authDoc.Status,
CreateTime: authDoc.CreateTime.Format("2006-01-02 15:04:05"),
})
}
}
// 3. 构建响应
resp = &types.GetAuthorizationDocumentByOrderResp{
Documents: documents,
}
return resp, nil
}

View File

@@ -0,0 +1,59 @@
package authorization
import (
"context"
"errors"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetAuthorizationDocumentLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetAuthorizationDocumentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAuthorizationDocumentLogic {
return &GetAuthorizationDocumentLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetAuthorizationDocumentLogic) GetAuthorizationDocument(req *types.GetAuthorizationDocumentReq) (resp *types.GetAuthorizationDocumentResp, err error) {
// 1. 从数据库获取授权书信息
authDoc, err := l.svcCtx.AuthorizationDocumentModel.FindOne(l.ctx, req.DocumentId)
if err != nil {
logx.Errorf("获取授权书失败: documentId=%d, error=%v", req.DocumentId, err)
return nil, err
}
// 2. 检查授权书状态
if authDoc.Status != "active" {
logx.Errorf("授权书状态异常: documentId=%d, status=%s", req.DocumentId, authDoc.Status)
return nil, errors.New("授权书不可用")
}
// 3. 构建完整文件URL
fullFileURL := l.svcCtx.AuthorizationService.GetFullFileURL(authDoc.FileUrl)
// 4. 构建响应
resp = &types.GetAuthorizationDocumentResp{
DocumentId: authDoc.Id,
UserId: authDoc.UserId,
OrderId: authDoc.OrderId,
QueryId: authDoc.QueryId,
FileName: authDoc.FileName,
FileUrl: fullFileURL,
FileSize: authDoc.FileSize,
FileType: authDoc.FileType,
Status: authDoc.Status,
CreateTime: authDoc.CreateTime.Format("2006-01-02 15:04:05"),
}
return resp, nil
}

View File

@@ -0,0 +1,57 @@
package notification
import (
"context"
"in-server/common/xerr"
"time"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetNotificationsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetNotificationsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNotificationsLogic {
return &GetNotificationsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetNotificationsLogic) GetNotifications() (resp *types.GetNotificationsResp, err error) {
// 获取今天的日期
now := time.Now()
// 获取开始和结束日期的时间戳
todayStart := now.Format("2006-01-02") + " 00:00:00"
todayEnd := now.Format("2006-01-02") + " 23:59:59"
// 构建查询条件
builder := l.svcCtx.GlobalNotificationsModel.SelectBuilder().
Where("status = ?", "active").
Where("(start_date IS NULL OR start_date <= ?)", todayEnd). // start_date 是 NULL 或者小于等于今天结束时间
Where("(end_date IS NULL OR end_date >= ?)", todayStart) // end_date 是 NULL 或者大于等于今天开始时间
notificationsModelList, findErr := l.svcCtx.GlobalNotificationsModel.FindAll(l.ctx, builder, "")
if findErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "全局通知, 查找通知失败, err:%+v", findErr)
}
var notifications []types.Notification
copyErr := copier.Copy(&notifications, &notificationsModelList)
if copyErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "全局通知, 复制结构体失败, err:%+v", copyErr)
}
return &types.GetNotificationsResp{Notifications: notifications}, nil
}

View File

@@ -0,0 +1,75 @@
package product
import (
"context"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/Masterminds/squirrel"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/mr"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetProductAppByEnLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetProductAppByEnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductAppByEnLogic {
return &GetProductAppByEnLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetProductAppByEnLogic) GetProductAppByEn(req *types.GetProductByEnRequest) (resp *types.ProductResponse, err error) {
productModel, err := l.svcCtx.ProductModel.FindOneByProductEn(l.ctx, req.ProductEn)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "产品查询, 查找产品错误: %v", err)
}
build := l.svcCtx.ProductFeatureModel.SelectBuilder().Where(squirrel.Eq{
"product_id": productModel.Id,
})
productFeatureAll, err := l.svcCtx.ProductFeatureModel.FindAll(l.ctx, build, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "产品查询, 查找产品关联错误: %v", err)
}
var product types.Product
err = copier.Copy(&product, productModel)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, 用户信息结构体复制失败, %v", err)
}
mr.MapReduceVoid(func(source chan<- interface{}) {
for _, productFeature := range productFeatureAll {
source <- productFeature.FeatureId
}
}, func(item interface{}, writer mr.Writer[*model.Feature], cancel func(error)) {
id := item.(string)
feature, findFeatureErr := l.svcCtx.FeatureModel.FindOne(l.ctx, id)
if findFeatureErr != nil {
logx.WithContext(l.ctx).Errorf("产品查询, 查找关联feature错误: %d, err:%v", id, findFeatureErr)
return
}
if feature != nil && feature.Id != "" {
writer.Write(feature)
}
}, func(pipe <-chan *model.Feature, cancel func(error)) {
for item := range pipe {
var feature types.Feature
_ = copier.Copy(&feature, item)
product.Features = append(product.Features, feature)
}
})
return &types.ProductResponse{Product: product}, nil
}

View File

@@ -0,0 +1,91 @@
package product
import (
"context"
"fmt"
"sort"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/Masterminds/squirrel"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/mr"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetProductByEnLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetProductByEnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductByEnLogic {
return &GetProductByEnLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetProductByEnLogic) GetProductByEn(req *types.GetProductByEnRequest) (resp *types.ProductResponse, err error) {
productModel, err := l.svcCtx.ProductModel.FindOneByProductEn(l.ctx, req.ProductEn)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "产品查询, 查找产品错误: %v", err)
}
build := l.svcCtx.ProductFeatureModel.SelectBuilder().Where(squirrel.Eq{
"product_id": productModel.Id,
})
productFeatureAll, err := l.svcCtx.ProductFeatureModel.FindAll(l.ctx, build, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "产品查询, 查找产品关联错误: %v", err)
}
// 创建featureId到sort的映射用于后续排序
featureSortMap := make(map[string]int64)
for _, productFeature := range productFeatureAll {
featureSortMap[fmt.Sprintf("%d", productFeature.FeatureId)] = productFeature.Sort
}
var product types.Product
err = copier.Copy(&product, productModel)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, 用户信息结构体复制失败, %v", err)
}
mr.MapReduceVoid(func(source chan<- interface{}) {
for _, productFeature := range productFeatureAll {
source <- productFeature.FeatureId
}
}, func(item interface{}, writer mr.Writer[*model.Feature], cancel func(error)) {
id := item.(string)
feature, findFeatureErr := l.svcCtx.FeatureModel.FindOne(l.ctx, id)
if findFeatureErr != nil {
logx.WithContext(l.ctx).Errorf("产品查询, 查找关联feature错误: %d, err:%v", id, findFeatureErr)
return
}
if feature != nil && feature.Id != "" {
writer.Write(feature)
}
}, func(pipe <-chan *model.Feature, cancel func(error)) {
for item := range pipe {
var feature types.Feature
_ = copier.Copy(&feature, item)
product.Features = append(product.Features, feature)
}
})
// 按照productFeature.Sort字段对features进行排序
sort.Slice(product.Features, func(i, j int) bool {
sortI := featureSortMap[product.Features[i].ID]
sortJ := featureSortMap[product.Features[j].ID]
return sortI < sortJ
})
return &types.ProductResponse{Product: product}, nil
}

View File

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

View File

@@ -0,0 +1,39 @@
package query
import (
"context"
"fmt"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type DownloadReportPdfLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDownloadReportPdfLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DownloadReportPdfLogic {
return &DownloadReportPdfLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DownloadReportPdfLogic) DownloadReportPdf(req *types.DownloadReportPdfReq) (resp *types.DownloadReportPdfResp, err error) {
pdfBytes, err := l.svcCtx.ReportPDFService.GetOrGenerateReportPDF(l.ctx, req.OrderId, req.OrderNo)
if err != nil {
logx.Errorf("生成报告 PDF 失败, orderId=%s, orderNo=%s, err=%v", req.OrderId, req.OrderNo, err)
return nil, fmt.Errorf("生成报告 PDF 失败: %w", err)
}
resp = &types.DownloadReportPdfResp{
FileName: fmt.Sprintf("report_%s.pdf", req.OrderNo),
Content: pdfBytes,
}
return resp, nil
}

View File

@@ -0,0 +1,153 @@
package query
import (
"context"
"database/sql"
"encoding/hex"
"encoding/json"
"fmt"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"in-server/pkg/lzkit/lzUtils"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
)
func ProcessQueryData(queryData sql.NullString, target *[]types.QueryItem, key []byte) error {
queryDataStr := lzUtils.NullStringToString(queryData)
if queryDataStr == "" {
return nil
}
decryptedData, decryptErr := crypto.AesDecrypt(queryDataStr, key)
if decryptErr != nil {
return decryptErr
}
var decryptedArray []map[string]interface{}
unmarshalErr := json.Unmarshal(decryptedData, &decryptedArray)
if unmarshalErr != nil {
return unmarshalErr
}
if len(*target) == 0 {
*target = make([]types.QueryItem, len(decryptedArray))
}
for i := 0; i < len(decryptedArray); i++ {
(*target)[i].Data = decryptedArray[i]
}
return nil
}
func ProcessQueryParams(QueryParams string, target *map[string]interface{}, key []byte) error {
decryptedData, decryptErr := crypto.AesDecrypt(QueryParams, key)
if decryptErr != nil {
return decryptErr
}
unmarshalErr := json.Unmarshal(decryptedData, target)
if unmarshalErr != nil {
return unmarshalErr
}
return nil
}
func UpdateFeatureAndProductFeature(ctx context.Context, svcCtx *svc.ServiceContext, productID string, target *[]types.QueryItem) error {
// 预先加载当前产品下所有 feature 的排序配置,避免在循环中反复查库
builder := svcCtx.ProductFeatureModel.SelectBuilder().Where("product_id = ?", productID)
productFeatures, err := svcCtx.ProductFeatureModel.FindAll(ctx, builder, "")
if err != nil {
return fmt.Errorf("查询 ProductFeatureModel 错误: %v", err)
}
featureSortMap := make(map[string]int, len(productFeatures))
for _, pf := range productFeatures {
featureSortMap[pf.FeatureId] = int(pf.Sort)
}
for i := 0; i < len(*target); i++ {
queryItem := &(*target)[i]
// Data 不是 map 或没有 apiID 时,不再报错/删除,直接跳过,保留原始数据
data, ok := queryItem.Data.(map[string]interface{})
if !ok {
continue
}
apiID, ok := data["apiID"].(string)
if !ok {
continue
}
// 查不到 feature 时也补充信息:使用 apiID 作为名称、排序默认 0
feature, err := svcCtx.FeatureModel.FindOneByApiId(ctx, apiID)
featureName := apiID
sort := 0
if err == nil && feature != nil {
featureName = feature.Name
if s, ok := featureSortMap[feature.Id]; ok {
sort = s
}
}
featureData := map[string]interface{}{
"featureName": featureName,
"sort": sort,
}
queryItem.Feature = featureData
}
return nil
}
func BuildEncryptedQuery(ctx context.Context, svcCtx *svc.ServiceContext, queryModel *model.Query) (string, error) {
var query types.Query
query.CreateTime = queryModel.CreateTime.Format("2006-01-02 15:04:05")
query.UpdateTime = queryModel.UpdateTime.Format("2006-01-02 15:04:05")
secretKey := svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 获取AES解密解药失败, %v", decodeErr)
}
processParamsErr := ProcessQueryParams(queryModel.QueryParams, &query.QueryParams, key)
if processParamsErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告参数处理失败: %v", processParamsErr)
}
processErr := ProcessQueryData(queryModel.QueryData, &query.QueryData, key)
if processErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结果处理失败: %v", processErr)
}
updateErr := UpdateFeatureAndProductFeature(ctx, svcCtx, queryModel.ProductId, &query.QueryData)
if updateErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结果处理失败: %v", updateErr)
}
err := copier.Copy(&query, queryModel)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结构体复制失败, %v", err)
}
product, err := svcCtx.ProductModel.FindOne(ctx, queryModel.ProductId)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 获取商品信息失败, %v", err)
}
query.Product = product.ProductEn
query.ProductName = product.ProductName
// 获取订单信息,添加订单金额
order, orderErr := svcCtx.OrderModel.FindOne(ctx, queryModel.OrderId)
if orderErr == nil {
query.Amount = order.Amount
}
queryBytes, marshalErr := json.Marshal(query)
if marshalErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 序列化查询结果失败: %v", marshalErr)
}
encryptedQuery, encryptErr := crypto.AesEncrypt(queryBytes, key)
if encryptErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 加密查询结果失败: %v", encryptErr)
}
return encryptedQuery, nil
}
// IsOrderAgent 兼容保留的旧接口,代理系统已下线,统一返回 false。
func IsOrderAgent(ctx context.Context, svcCtx *svc.ServiceContext, userId string, orderId string) (bool, error) {
return false, nil
}

View File

@@ -0,0 +1,76 @@
package query
import (
"context"
"in-server/common/ctxdata"
"in-server/common/xerr"
"github.com/pkg/errors"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryDetailByOrderIdLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryDetailByOrderIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryDetailByOrderIdLogic {
return &QueryDetailByOrderIdLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryDetailByOrderIdLogic) QueryDetailByOrderId(req *types.QueryDetailByOrderIdReq) (resp string, err error) {
// 获取当前用户ID
userId, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %v", err)
}
// 获取订单信息
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, req.OrderId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return "", errors.Wrapf(xerr.NewErrCode(xerr.LOGIC_QUERY_NOT_FOUND), "报告查询, 订单不存在: %v", err)
}
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询, 查找报告错误: %v", err)
}
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userId)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询, 查找用户错误: %v", err)
}
if user.Inside != 1 {
// 安全验证:确保订单属于当前用户,或为该订单的代理
if order.UserId != userId {
isAgent, aerr := IsOrderAgent(l.ctx, l.svcCtx, userId, order.Id)
if aerr != nil {
return "", aerr
}
if !isAgent {
return "", errors.Wrapf(xerr.NewErrCode(xerr.LOGIC_QUERY_NOT_FOUND), "无权查看此订单报告")
}
}
}
// 检查订单状态
if order.Status != "paid" {
return "", errors.Wrapf(xerr.NewErrMsg("订单未支付,无法查看报告"), "")
}
queryModel, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, req.OrderId)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询, 查找报告错误: %v", err)
}
respStr, buildErr := BuildEncryptedQuery(l.ctx, l.svcCtx, queryModel)
if buildErr != nil {
return "", buildErr
}
return respStr, nil
}

View File

@@ -0,0 +1,58 @@
package query
import (
"context"
"in-server/common/xerr"
"github.com/pkg/errors"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryDetailByOrderIdPublicLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryDetailByOrderIdPublicLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryDetailByOrderIdPublicLogic {
return &QueryDetailByOrderIdPublicLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryDetailByOrderIdPublicLogic) QueryDetailByOrderIdPublic(req *types.QueryDetailByOrderIdReq) (resp string, err error) {
// 公共接口:仅校验订单存在且已支付,不做用户绑定校验,用于【生成 PDF】等内部场景
// 获取订单信息
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, req.OrderId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return "", errors.Wrapf(xerr.NewErrCode(xerr.LOGIC_QUERY_NOT_FOUND), "报告查询(公共), 订单不存在: %v", err)
}
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询(公共), 查找订单错误: %v", err)
}
// 检查订单状态:未支付则不返回报告
if order.Status != "paid" {
return "", errors.Wrapf(xerr.NewErrMsg("订单未支付,无法查看报告"), "")
}
// 查找对应查询记录并构建加密响应,复用已有工具函数
queryModel, qErr := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, req.OrderId)
if qErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询(公共), 查找报告错误: %v", qErr)
}
respStr, buildErr := BuildEncryptedQuery(l.ctx, l.svcCtx, queryModel)
if buildErr != nil {
return "", buildErr
}
return respStr, nil
}

View File

@@ -0,0 +1,72 @@
package query
import (
"context"
"in-server/common/ctxdata"
"in-server/common/xerr"
"github.com/pkg/errors"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryDetailByOrderNoLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryDetailByOrderNoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryDetailByOrderNoLogic {
return &QueryDetailByOrderNoLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryDetailByOrderNoLogic) QueryDetailByOrderNo(req *types.QueryDetailByOrderNoReq) (resp string, err error) {
// 获取当前用户ID
userId, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %v", err)
}
// 获取订单信息
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return "", errors.Wrapf(xerr.NewErrCode(xerr.LOGIC_QUERY_NOT_FOUND), "报告查询, 订单不存在: %v", err)
}
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询, 查找报告错误: %v", err)
}
// 安全验证:确保订单属于当前用户,或为该订单的代理
if order.UserId != userId {
isAgent, aerr := IsOrderAgent(l.ctx, l.svcCtx, userId, order.Id)
if aerr != nil {
return "", aerr
}
if !isAgent {
return "", errors.Wrapf(xerr.NewErrCode(xerr.LOGIC_QUERY_NOT_FOUND), "无权查看此订单报告")
}
}
// 检查订单状态
if order.Status != "paid" {
return "", errors.Wrapf(xerr.NewErrMsg("订单未支付,无法查看报告"), "")
}
queryModel, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, order.Id)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询, 查找报告错误: %v", err)
}
respStr, buildErr := BuildEncryptedQuery(l.ctx, l.svcCtx, queryModel)
if buildErr != nil {
return "", buildErr
}
return respStr, nil
}

View File

@@ -0,0 +1,203 @@
package query
import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"strings"
"github.com/bytedance/sonic"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
// comboExampleDataItem 组合包示例 Content 解密后的单条结构(已转换格式),与 APIResponseData 一致
type comboExampleDataItem struct {
ApiID string `json:"apiID"`
Data json.RawMessage `json:"data"`
Success bool `json:"success"`
Timestamp string `json:"timestamp"`
}
// comboExampleRawResponse 组合包数据源原始返回格式:{ "responses": [ { "api_code", "success", "data" } ] }
type comboExampleRawResponse struct {
Responses []struct {
ApiCode string `json:"api_code"`
Success bool `json:"success"`
Data json.RawMessage `json:"data"`
} `json:"responses"`
}
type QueryExampleLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryExampleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryExampleLogic {
return &QueryExampleLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryExampleLogic) QueryExample(req *types.QueryExampleReq) (resp string, err error) {
// 根据产品特性标识获取产品信息
product, err := l.svcCtx.ProductModel.FindOneByProductEn(l.ctx, req.Feature)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "示例报告, 获取商品信息失败, %v", err)
}
secretKeyHex := l.svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKeyHex)
if decodeErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "示例报告, 解析AES密钥失败, %v", decodeErr)
}
// 创建一个空的Query结构体来存储结果
query := types.Query{
Product: product.ProductEn,
ProductName: product.ProductName,
QueryData: make([]types.QueryItem, 0),
QueryParams: make(map[string]interface{}),
}
query.QueryParams = map[string]interface{}{
"id_card": "45000000000000000",
"mobile": "13700000000",
"name": "张老三",
}
// 查询ProductFeatureModel获取产品相关的功能列表
builder := l.svcCtx.ProductFeatureModel.SelectBuilder().Where("product_id = ?", product.Id)
productFeatures, err := l.svcCtx.ProductFeatureModel.FindAll(l.ctx, builder, "")
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "示例报告, 查询 ProductFeatureModel 错误: %v", err)
}
// 从每个启用的特性获取示例数据并合并
for _, pf := range productFeatures {
if pf.Enable != 1 {
continue // 跳过未启用的特性
}
// 根据特性ID查找示例数据
example, err := l.svcCtx.ExampleModel.FindOneByFeatureId(l.ctx, pf.FeatureId)
if err != nil {
logx.Infof("示例报告, 特性ID %s 无示例数据: %v", pf.FeatureId, err)
continue // 如果没有示例数据就跳过
}
// 获取对应的Feature信息
feature, err := l.svcCtx.FeatureModel.FindOne(l.ctx, pf.FeatureId)
if err != nil {
logx.Infof("示例报告, 无法获取特性ID %s 的信息: %v", pf.FeatureId, err)
continue
}
// 组合包api_id 前四位为 COMB示例 Content 支持两种格式,按子模块展开为多个 Tab
// 格式1[]comboExampleDataItem 即 [ { "apiID", "data", "success", "timestamp" }, ... ]
// 格式2数据源原始格式 { "responses": [ { "api_code", "success", "data" }, ... ] }
if strings.HasPrefix(feature.ApiId, "COMB") {
if example.Content == "000" {
continue
}
decryptedData, decryptErr := crypto.AesDecrypt(example.Content, key)
if decryptErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "示例报告, 组合包解密失败: %v", decryptErr)
}
decryptedBytes := decryptedData
type expandItem struct {
ApiID string
Data json.RawMessage
Success bool
Timestamp string
}
var itemsToExpand []expandItem
trimmed := bytes.TrimSpace(decryptedBytes)
if len(trimmed) > 0 && trimmed[0] == '[' {
var comboItems []comboExampleDataItem
if err := sonic.Unmarshal(decryptedBytes, &comboItems); err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "示例报告, 组合包示例解析失败: %v", err)
}
for _, item := range comboItems {
itemsToExpand = append(itemsToExpand, expandItem{item.ApiID, item.Data, item.Success, item.Timestamp})
}
} else {
var raw comboExampleRawResponse
if err := sonic.Unmarshal(decryptedBytes, &raw); err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "示例报告, 组合包示例解析失败(非数组且非responses格式): %v", err)
}
for _, r := range raw.Responses {
itemsToExpand = append(itemsToExpand, expandItem{r.ApiCode, r.Data, r.Success, ""})
}
}
for i, item := range itemsToExpand {
subFeature, subErr := l.svcCtx.FeatureModel.FindOneByApiId(l.ctx, item.ApiID)
featureName := item.ApiID
if subErr == nil && subFeature != nil {
featureName = subFeature.Name
}
ts := item.Timestamp
if ts == "" {
ts = "2020-01-01 00:00:00"
}
query.QueryData = append(query.QueryData, types.QueryItem{
Feature: map[string]interface{}{
"featureName": featureName,
"sort": pf.Sort*1000 + int64(i),
},
Data: map[string]interface{}{
"apiID": item.ApiID,
"data": item.Data,
"success": item.Success,
"timestamp": ts,
},
})
}
continue
}
var queryItem types.QueryItem
// 解密查询数据
// 解析示例内容
if example.Content == "000" {
queryItem.Data = example.Content
} else {
// 解密数据
decryptedData, decryptErr := crypto.AesDecrypt(example.Content, key)
if decryptErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "示例报告, 解密数据失败: %v", decryptErr)
}
err = sonic.Unmarshal([]byte(decryptedData), &queryItem.Data)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "示例报告, 解析示例内容失败: %v", err)
}
}
// 添加特性信息
queryItem.Feature = map[string]interface{}{
"featureName": feature.Name,
"sort": pf.Sort,
}
// 添加到查询数据中
query.QueryData = append(query.QueryData, queryItem)
}
queryBytes, marshalErr := sonic.Marshal(query)
if marshalErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "示例报告, 序列化查询结果失败: %v", marshalErr)
}
encryptedQuery, encryptErr := crypto.AesEncrypt(queryBytes, key)
if encryptErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "示例报告, 加密查询结果失败: %v", encryptErr)
}
return encryptedQuery, nil
}

View File

@@ -0,0 +1,111 @@
package query
import (
"context"
"encoding/hex"
"encoding/json"
"time"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/ctxdata"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryGenerateShareLinkLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryGenerateShareLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryGenerateShareLinkLogic {
return &QueryGenerateShareLinkLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryGenerateShareLinkLogic) QueryGenerateShareLink(req *types.QueryGenerateShareLinkReq) (resp *types.QueryGenerateShareLinkResp, err error) {
userId, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 获取用户ID失败: %v", err)
}
// 检查参数
if (req.OrderId == nil || *req.OrderId == "") && (req.OrderNo == nil || *req.OrderNo == "") {
return nil, errors.Wrapf(xerr.NewErrMsg("订单ID和订单号不能同时为空"), "")
}
var order *model.Order
// 优先使用OrderId查询
if req.OrderId != nil && *req.OrderId != "" {
order, err = l.svcCtx.OrderModel.FindOne(l.ctx, *req.OrderId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg("订单不存在"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 获取订单失败: %v", err)
}
} else if req.OrderNo != nil && *req.OrderNo != "" {
// 使用OrderNo查询
order, err = l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, *req.OrderNo)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg("订单不存在"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 获取订单失败: %v", err)
}
} else {
return nil, errors.Wrapf(xerr.NewErrMsg("订单ID和订单号不能同时为空"), "")
}
if order.Status != model.OrderStatusPaid {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 订单未支付")
}
query, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, order.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 获取查询失败: %v", err)
}
if query.QueryState != model.QueryStateSuccess {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 查询未成功")
}
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 获取用户失败: %v", err)
}
if user.Inside != 1 {
if order.UserId != userId {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 无权操作此订单")
}
}
expireAt := time.Now().Add(time.Duration(l.svcCtx.Config.Query.ShareLinkExpire) * time.Second)
payload := types.QueryShareLinkPayload{
OrderId: order.Id, // 使用查询到的订单ID
ExpireAt: expireAt.Unix(),
}
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, err := hex.DecodeString(secretKey)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 解密失败: %v", err)
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 序列化失败: %v", err)
}
encryptedPayload, err := crypto.AesEncryptURL(payloadBytes, key)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成分享链接, 加密失败: %v", err)
}
return &types.QueryGenerateShareLinkResp{
ShareLink: encryptedPayload,
}, nil
}

View File

@@ -0,0 +1,82 @@
package query
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/ctxdata"
"in-server/common/xerr"
"github.com/Masterminds/squirrel"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryListLogic {
return &QueryListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryListLogic) QueryList(req *types.QueryListReq) (resp *types.QueryListResp, err error) {
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
if getUidErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告列表查询, 获取用户信息失败, %+v", getUidErr)
}
// 直接构建查询query表的条件
build := l.svcCtx.QueryModel.SelectBuilder().Where(squirrel.Eq{
"user_id": userID,
})
// 直接从query表分页查询
queryList, total, err := l.svcCtx.QueryModel.FindPageListByPageWithTotal(l.ctx, build, req.Page, req.PageSize, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告列表查询, 查找报告列表错误, %+v", err)
}
var list []types.Query
if len(queryList) > 0 {
for _, queryModel := range queryList {
var query types.Query
query.CreateTime = queryModel.CreateTime.Format("2006-01-02 15:04:05")
query.UpdateTime = queryModel.UpdateTime.Format("2006-01-02 15:04:05")
copyErr := copier.Copy(&query, queryModel)
if copyErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告列表查询, 报告结构体复制失败, %+v", err)
}
product, findProductErr := l.svcCtx.ProductModel.FindOne(l.ctx, queryModel.ProductId)
if findProductErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告列表查询, 获取商品信息失败, %+v", err)
}
// 检查订单状态,如果订单已退款,则设置查询状态为已退款
order, findOrderErr := l.svcCtx.OrderModel.FindOne(l.ctx, queryModel.OrderId)
if findOrderErr == nil {
if order.Status == model.OrderStatusRefunded {
query.QueryState = model.QueryStateRefunded
}
query.OrderNo = order.OrderNo
query.Amount = order.Amount
}
query.ProductName = product.ProductName
query.Product = product.ProductEn
list = append(list, query)
}
}
return &types.QueryListResp{
Total: total,
List: list,
}, nil
}

View File

@@ -0,0 +1,63 @@
package query
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/ctxdata"
"in-server/common/xerr"
"encoding/json"
"fmt"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryProvisionalOrderLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryProvisionalOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryProvisionalOrderLogic {
return &QueryProvisionalOrderLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryProvisionalOrderLogic) QueryProvisionalOrder(req *types.QueryProvisionalOrderReq) (resp *types.QueryProvisionalOrderResp, err error) {
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
if getUidErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取临时订单, 获取用户信息失败, %+v", getUidErr)
}
redisKey := fmt.Sprintf("%d:%s", userID, req.Id)
cache, cacheErr := l.svcCtx.Redis.GetCtx(l.ctx, redisKey)
if cacheErr != nil {
return nil, cacheErr
}
var data types.QueryCache
err = json.Unmarshal([]byte(cache), &data)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取临时订单, 解析缓存内容失败, %v", err)
}
productModel, err := l.svcCtx.ProductModel.FindOneByProductEn(l.ctx, data.Product)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取临时订单, 查找产品错误: %v", err)
}
var product types.Product
err = copier.Copy(&product, productModel)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取临时订单, 用户信息结构体复制失败: %v", err)
}
return &types.QueryProvisionalOrderResp{
Name: data.Name,
IdCard: data.Name,
Mobile: data.Mobile,
Product: product,
}, nil
}

View File

@@ -0,0 +1,44 @@
package query
import (
"context"
"in-server/common/xerr"
"github.com/pkg/errors"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryRetryLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryRetryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryRetryLogic {
return &QueryRetryLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryRetryLogic) QueryRetry(req *types.QueryRetryReq) (resp *types.QueryRetryResp, err error) {
query, err := l.svcCtx.QueryModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询重试, 查找报告失败, %v", err)
}
if query.QueryState == "success" {
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.LOGIN_FAILED, "该报告不能重试"), "报告查询重试, 该报告不能重试, %d", query.Id)
}
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(query.OrderId); asyncErr != nil {
logx.Errorf("异步任务调度失败: %v", asyncErr)
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询重试, 异步任务调度失败, %+v", asyncErr)
}
return
}

View File

@@ -0,0 +1,33 @@
package query
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryServiceAgentLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryServiceAgentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryServiceAgentLogic {
return &QueryServiceAgentLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryServiceAgentLogic) QueryServiceAgent(req *types.QueryServiceReq) (resp *types.QueryServiceResp, err error) {
if req.AgentIdentifier != "" {
l.ctx = context.WithValue(l.ctx, "agentIdentifier", req.AgentIdentifier)
} else if req.App {
l.ctx = context.WithValue(l.ctx, "app", req.App)
}
proxy := NewQueryServiceLogic(l.ctx, l.svcCtx)
return proxy.PreprocessLogic(req, req.Product)
}

View File

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

View File

@@ -0,0 +1,847 @@
package query
import (
"context"
"crypto/rand"
"database/sql"
"encoding/hex"
"encoding/json"
"fmt"
"in-server/app/main/api/internal/service"
"in-server/common/ctxdata"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"in-server/pkg/lzkit/validator"
"os"
"strconv"
"sync/atomic"
"time"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/redis"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryServiceLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryServiceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryServiceLogic {
return &QueryServiceLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryServiceLogic) QueryService(req *types.QueryServiceReq) (resp *types.QueryServiceResp, err error) {
if req.AgentIdentifier != "" {
l.ctx = context.WithValue(l.ctx, "agentIdentifier", req.AgentIdentifier)
} else if req.App {
l.ctx = context.WithValue(l.ctx, "app", req.App)
}
return l.PreprocessLogic(req, req.Product)
}
var productProcessors = map[string]func(*QueryServiceLogic, *types.QueryServiceReq) (*types.QueryServiceResp, error){
"marriage": (*QueryServiceLogic).ProcessMarriageLogic,
"homeservice": (*QueryServiceLogic).ProcessHomeServiceLogic,
"riskassessment": (*QueryServiceLogic).ProcessRiskAssessmentLogic,
"companyinfo": (*QueryServiceLogic).ProcessCompanyInfoLogic,
"rentalinfo": (*QueryServiceLogic).ProcessRentalInfoLogic,
"preloanbackgroundcheck": (*QueryServiceLogic).ProcessPreLoanBackgroundCheckLogic,
"backgroundcheck": (*QueryServiceLogic).ProcessBackgroundCheckLogic,
"personalData": (*QueryServiceLogic).ProcessPersonalDataLogic,
"consumerFinanceReport": (*QueryServiceLogic).ProcessConsumerFinanceReportLogic,
}
func (l *QueryServiceLogic) PreprocessLogic(req *types.QueryServiceReq, product string) (*types.QueryServiceResp, error) {
if processor, exists := productProcessors[product]; exists {
return processor(l, req) // 调用对应的处理函数
}
return nil, errors.New("未找到相应的处理程序")
}
func (l *QueryServiceLogic) ProcessMarriageLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
// AES解密
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
// 验证参数
var data types.MarriageReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
}
// 验证验证码
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
// 验证三要素
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
// 缓存
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
userID, err := l.GetOrCreateUser()
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
}
cacheNo, cacheDataErr := l.CacheData(params, "marriage", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %v", err)
}
// 获取当前时间戳
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
}, nil
}
// 处理家政服务相关逻辑
func (l *QueryServiceLogic) ProcessHomeServiceLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
// AES解密
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
// 验证参数
var data types.HomeServiceReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
}
// 验证验证码
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
// 验证三要素
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
// 缓存
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
userID, err := l.GetOrCreateUser()
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
}
cacheNo, cacheDataErr := l.CacheData(params, "homeservice", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
}
// 获取当前时间戳
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
}, nil
}
// 处理风险评估相关逻辑
func (l *QueryServiceLogic) ProcessRiskAssessmentLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
// AES解密
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
// 验证参数
var data types.RiskAssessmentReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
}
// 验证三要素
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
// 缓存
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
userID, err := l.GetOrCreateUser()
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
}
cacheNo, cacheDataErr := l.CacheData(params, "riskassessment", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
// 立即创建订单并调用 apirequest 出结果(无需支付)
orderId, orderNo, apiErr := l.createOrderAndRunApiRequest(userID, cacheNo, params, "riskassessment")
if apiErr != nil {
return nil, apiErr
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
}
// 获取当前时间戳
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
OrderId: orderId,
OrderNo: orderNo,
}, nil
}
// 处理公司信息查询相关逻辑
func (l *QueryServiceLogic) ProcessCompanyInfoLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
// AES解密
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
// 验证参数
var data types.CompanyInfoReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
}
// 验证验证码
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
// 验证三要素
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
// 缓存
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
userID, err := l.GetOrCreateUser()
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
}
cacheNo, cacheDataErr := l.CacheData(params, "companyinfo", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
}
// 获取当前时间戳
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
}, nil
}
// 处理租赁信息查询相关逻辑
func (l *QueryServiceLogic) ProcessRentalInfoLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
// AES解密
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
// 验证参数
var data types.RentalInfoReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
}
// 验证验证码
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
// 验证三要素
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
// 缓存
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
userID, err := l.GetOrCreateUser()
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
}
cacheNo, cacheDataErr := l.CacheData(params, "rentalinfo", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
}
// 获取当前时间戳
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
}, nil
}
// 处理贷前背景检查相关逻辑
func (l *QueryServiceLogic) ProcessPreLoanBackgroundCheckLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
// AES解密
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
// 验证参数
var data types.PreLoanBackgroundCheckReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
}
// 验证三要素
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
// 缓存
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
userID, err := l.GetOrCreateUser()
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
}
cacheNo, cacheDataErr := l.CacheData(params, "preloanbackgroundcheck", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
// 立即创建订单并调用 apirequest 出结果(无需支付)
orderId, orderNo, apiErr := l.createOrderAndRunApiRequest(userID, cacheNo, params, "preloanbackgroundcheck")
if apiErr != nil {
return nil, apiErr
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
}
// 获取当前时间戳
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
OrderId: orderId,
OrderNo: orderNo,
}, nil
}
// 处理人事背调相关逻辑
func (l *QueryServiceLogic) ProcessBackgroundCheckLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
// AES解密
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
// 验证参数
var data types.BackgroundCheckReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
}
// 验证验证码
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
// 验证三要素
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
// 缓存
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
userID, err := l.GetOrCreateUser()
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
}
cacheNo, cacheDataErr := l.CacheData(params, "backgroundcheck", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
}
// 获取当前时间戳
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
}, nil
}
func (l *QueryServiceLogic) ProcessPersonalDataLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
// AES解密
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
// 验证参数
var data types.PersonalDataReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
}
// 验证验证码
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
// 验证三要素
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
// 缓存
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
userID, err := l.GetOrCreateUser()
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
}
cacheNo, cacheDataErr := l.CacheData(params, "personalData", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
}
// 获取当前时间戳
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
}, nil
}
func (l *QueryServiceLogic) ProcessConsumerFinanceReportLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
// AES解密
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
// 验证参数
var data types.ConsumerFinanceReportReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
}
// 验证验证码
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
// 验证三要素
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
// 缓存
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
userID, err := l.GetOrCreateUser()
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
}
cacheNo, cacheDataErr := l.CacheData(params, "consumerFinanceReport", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
}
// 获取当前时间戳
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
}, nil
}
func (l *QueryServiceLogic) DecryptData(data string) ([]byte, error) {
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "密钥获取失败: %+v", decodeErr)
}
decryptData, aesDecryptErr := crypto.AesDecrypt(data, key)
if aesDecryptErr != nil || len(decryptData) == 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密失败: %+v", aesDecryptErr)
}
return decryptData, nil
}
// 验证验证码
func (l *QueryServiceLogic) VerifyCode(mobile string, code string) error {
// 开发环境下跳过验证码校验
if os.Getenv("ENV") == "development" {
return nil
}
if code == "278712" {
return nil
}
secretKey := l.svcCtx.Config.Encrypt.SecretKey
encryptedMobile, err := crypto.EncryptMobile(mobile, secretKey)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %+v", err)
}
codeRedisKey := fmt.Sprintf("%s:%s", "query", encryptedMobile)
cacheCode, err := l.svcCtx.Redis.Get(codeRedisKey)
if err != nil {
if errors.Is(err, redis.Nil) {
return errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "验证码过期: %s", mobile)
}
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "读取验证码redis缓存失败, mobile: %s, err: %+v", mobile, err)
}
if cacheCode != code {
return errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "验证码不正确: %s", mobile)
}
return nil
}
// 二三要素验证
func (l *QueryServiceLogic) Verify(Name string, IDCard string, Mobile string) error {
// 开发环境下跳过人/三要素校验
if os.Getenv("ENV") == "development" {
return nil
}
if !l.svcCtx.Config.SystemConfig.ThreeVerify {
twoVerification := service.TwoFactorVerificationRequest{
Name: Name,
IDCard: IDCard,
}
verification, err := l.svcCtx.VerificationService.TwoFactorVerification(twoVerification)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "二要素校验失败: %v", err)
}
if !verification.Passed {
return errors.Wrapf(xerr.NewErrCodeMsg(xerr.SERVER_COMMON_ERROR, verification.Err.Error()), "二要素校验不通过: %v", err)
}
} else {
// 三要素校验
threeVerification := service.ThreeFactorVerificationRequest{
Name: Name,
IDCard: IDCard,
Mobile: Mobile,
}
verification, err := l.svcCtx.VerificationService.ThreeFactorVerification(threeVerification)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "三要素校验失败: %v", err)
}
if !verification.Passed {
return errors.Wrapf(xerr.NewErrCodeMsg(xerr.SERVER_COMMON_ERROR, verification.Err.Error()), "三要素校验不通过: %v", err)
}
}
return nil
}
// 缓存
func (l *QueryServiceLogic) CacheData(params map[string]interface{}, Product string, userID string) (string, error) {
agentIdentifier, _ := l.ctx.Value("agentIdentifier").(string)
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 获取AES密钥失败: %+v", decodeErr)
}
paramsMarshal, marshalErr := json.Marshal(params)
if marshalErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 序列化参数失败: %+v", marshalErr)
}
encryptParams, aesEncryptErr := crypto.AesEncrypt(paramsMarshal, key)
if aesEncryptErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 加密参数失败: %+v", aesEncryptErr)
}
queryCache := types.QueryCacheLoad{
Params: encryptParams,
Product: Product,
AgentIdentifier: agentIdentifier,
}
jsonData, marshalErr := json.Marshal(queryCache)
if marshalErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 序列化参数失败: %+v", marshalErr)
}
outTradeNo := "Q_" + uuid.NewString()
redisKey := fmt.Sprintf(types.QueryCacheKey, userID, outTradeNo)
cacheErr := l.svcCtx.Redis.SetexCtx(l.ctx, redisKey, string(jsonData), int(2*time.Hour))
if cacheErr != nil {
return "", cacheErr
}
return outTradeNo, nil
}
// GetOrCreateUser 获取或创建用户
// 1. 如果已登录,使用当前登录用户
// 2. 如果未登录创建临时用户UUID用户
// 注意:查询服务不负责手机号绑定,手机号绑定由用户在其他地方自主选择
func (l *QueryServiceLogic) GetOrCreateUser() (string, error) {
// 获取当前登录态
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
logx.Infof("claims: %+v", claims)
if err != nil && !errors.Is(err, ctxdata.ErrNoInCtx) {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败: %v", err)
}
// 如果已登录,使用当前登录用户
if claims != nil {
return claims.UserId, nil
}
// 未登录创建临时用户UUID用户
userID, err := l.svcCtx.UserService.RegisterUUIDUser(l.ctx)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建临时用户失败: %v", err)
}
return userID, nil
}
// createOrderAndRunApiRequest 立即创建订单与查询记录、调用 apirequest 并写回结果,返回 orderId、orderNo
func (l *QueryServiceLogic) createOrderAndRunApiRequest(userID, cacheNo string, params map[string]interface{}, productEn string) (orderId, orderNo string, err error) {
product, productErr := l.svcCtx.ProductModel.FindOneByProductEn(l.ctx, productEn)
if productErr != nil {
return "", "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询产品失败: %v", productErr)
}
paramsBytes, marshalErr := json.Marshal(params)
if marshalErr != nil {
return "", "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "序列化参数失败: %v", marshalErr)
}
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return "", "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取AES密钥失败: %v", decodeErr)
}
encryptedParams, aesErr := crypto.AesEncrypt(paramsBytes, key)
if aesErr != nil {
return "", "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密参数失败: %v", aesErr)
}
order := &model.Order{
OrderNo: l.GenerateOutTradeNo(),
UserId: userID,
ProductId: product.Id,
PaymentPlatform: "none",
PaymentScene: "app",
Amount: 0,
Status: model.OrderStatusPaid,
}
if _, insertOrderErr := l.svcCtx.OrderModel.Insert(l.ctx, nil, order); insertOrderErr != nil {
return "", "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建订单失败: %v", insertOrderErr)
}
queryRow := &model.Query{
OrderId: order.Id,
UserId: userID,
ProductId: product.Id,
QueryParams: encryptedParams,
QueryData: sql.NullString{},
QueryState: model.QueryStatePending,
}
if _, insertQueryErr := l.svcCtx.QueryModel.Insert(l.ctx, nil, queryRow); insertQueryErr != nil {
return "", "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建查询记录失败: %v", insertQueryErr)
}
apiResp, processErr := l.svcCtx.ApiRequestService.ProcessRequests(paramsBytes, product.Id)
if processErr != nil {
queryRow.QueryState = model.QueryStateFailed
_ = l.svcCtx.QueryModel.UpdateWithVersion(l.ctx, nil, queryRow)
return "", "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "调用查询接口失败: %v", processErr)
}
encryptedData, encryptErr := crypto.AesEncrypt(apiResp, key)
if encryptErr != nil {
queryRow.QueryState = model.QueryStateFailed
_ = l.svcCtx.QueryModel.UpdateWithVersion(l.ctx, nil, queryRow)
return "", "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密结果失败: %v", encryptErr)
}
queryRow.QueryData = sql.NullString{String: encryptedData, Valid: true}
queryRow.QueryState = model.QueryStateSuccess
if updateErr := l.svcCtx.QueryModel.UpdateWithVersion(l.ctx, nil, queryRow); updateErr != nil {
return "", "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "更新查询结果失败: %v", updateErr)
}
return order.Id, order.OrderNo, nil
}
// 添加全局原子计数器
var alipayOrderCounter uint32 = 0
// GenerateOutTradeNo 生成唯一订单号的函数 - 优化版本
func (l *QueryServiceLogic) GenerateOutTradeNo() string {
// 获取当前时间戳(毫秒级)
timestamp := time.Now().UnixMilli()
timeStr := strconv.FormatInt(timestamp, 10)
// 原子递增计数器
counter := atomic.AddUint32(&alipayOrderCounter, 1)
// 生成4字节真随机数
randomBytes := make([]byte, 4)
_, err := rand.Read(randomBytes)
if err != nil {
// 如果随机数生成失败,回退到使用时间纳秒数据
randomBytes = []byte(strconv.FormatInt(time.Now().UnixNano()%1000000, 16))
}
randomHex := hex.EncodeToString(randomBytes)
// 组合所有部分: 前缀 + 时间戳 + 计数器 + 随机数
orderNo := fmt.Sprintf("%s%06x%s", timeStr[:10], counter%0xFFFFFF, randomHex[:6])
// 确保长度不超过32字符大多数支付平台的限制
if len(orderNo) > 32 {
orderNo = orderNo[:32]
}
return orderNo
}

View File

@@ -0,0 +1,199 @@
package query
import (
"context"
"encoding/hex"
"fmt"
"time"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"github.com/bytedance/sonic"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type QueryShareDetailLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQueryShareDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryShareDetailLogic {
return &QueryShareDetailLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryShareDetailLogic) QueryShareDetail(req *types.QueryShareDetailReq) (resp string, err error) {
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 获取AES解密解药失败, %v", decodeErr)
}
decryptedID, decryptErr := crypto.AesDecryptURL(req.Id, key)
if decryptErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 解密数据失败: %v", decryptErr)
}
var payload types.QueryShareLinkPayload
err = sonic.Unmarshal(decryptedID, &payload)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 解密数据失败: %v", err)
}
type shareDetail struct {
Status string `json:"status"`
Query *types.Query `json:"query,omitempty"`
}
// 检查分享链接是否过期
now := time.Now().Unix()
if now > payload.ExpireAt {
expiredResp := shareDetail{
Status: "expired",
}
encryptedExpired, encryptErr := l.encryptShareDetail(expiredResp, key)
if encryptErr != nil {
return "", encryptErr
}
return encryptedExpired, nil
}
// 获取订单信息
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, payload.OrderId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return "", errors.Wrapf(xerr.NewErrCode(xerr.LOGIC_QUERY_NOT_FOUND), "报告查询, 订单不存在: %v", err)
}
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询, 查找报告错误: %v", err)
}
// 检查订单状态
if order.Status != "paid" {
return "", errors.Wrapf(xerr.NewErrMsg("订单未支付,无法查看报告"), "")
}
// 获取报告信息
queryModel, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, order.Id)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告查询, 查找报告错误: %v", err)
}
var query types.Query
query.CreateTime = queryModel.CreateTime.Format("2006-01-02 15:04:05")
query.UpdateTime = queryModel.UpdateTime.Format("2006-01-02 15:04:05")
processParamsErr := ProcessQueryParams(queryModel.QueryParams, &query.QueryParams, key)
if processParamsErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告参数处理失败: %v", processParamsErr)
}
processErr := ProcessQueryData(queryModel.QueryData, &query.QueryData, key)
if processErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结果处理失败: %v", processErr)
}
updateFeatureAndProductFeatureErr := l.UpdateFeatureAndProductFeature(queryModel.ProductId, &query.QueryData)
if updateFeatureAndProductFeatureErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结果处理失败: %v", updateFeatureAndProductFeatureErr)
}
// 复制报告数据
err = copier.Copy(&query, queryModel)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 报告结构体复制失败, %v", err)
}
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, queryModel.ProductId)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 获取商品信息失败, %v", err)
}
query.ProductName = product.ProductName
query.Product = product.ProductEn
// 从订单获取金额
query.Amount = order.Amount
successResp := shareDetail{
Status: "success",
Query: &query,
}
encryptedSuccess, encryptErr := l.encryptShareDetail(successResp, key)
if encryptErr != nil {
return "", encryptErr
}
return encryptedSuccess, nil
}
func (l *QueryShareDetailLogic) encryptShareDetail(detail interface{}, key []byte) (string, error) {
payloadBytes, marshalErr := sonic.Marshal(detail)
if marshalErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 序列化查询结果失败: %v", marshalErr)
}
encrypted, encryptErr := crypto.AesEncrypt(payloadBytes, key)
if encryptErr != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 加密查询结果失败: %v", encryptErr)
}
return encrypted, nil
}
func (l *QueryShareDetailLogic) UpdateFeatureAndProductFeature(productID string, target *[]types.QueryItem) 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{}
// foundFeature := false
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,52 @@
package query
import (
"context"
"encoding/json"
"in-server/common/xerr"
"github.com/pkg/errors"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type QuerySingleTestLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQuerySingleTestLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QuerySingleTestLogic {
return &QuerySingleTestLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QuerySingleTestLogic) QuerySingleTest(req *types.QuerySingleTestReq) (resp *types.QuerySingleTestResp, err error) {
//featrueModel, err := l.svcCtx.FeatureModel.FindOneByApiId(l.ctx, req.Api)
//if err != nil {
// return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "单查测试, 获取接口失败 : %d", err)
//}
marshalParams, err := json.Marshal(req.Params)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "单查测试, 序列化参数失败 : %d", err)
}
apiResp, err := l.svcCtx.ApiRequestService.PreprocessRequestApi(marshalParams, req.Api)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "单查测试, 获取接口失败 : %d", err)
}
var respData interface{}
err = json.Unmarshal(apiResp, &respData)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "单查测试, 反序列化接口失败 : %d", err)
}
return &types.QuerySingleTestResp{
Data: respData,
Api: req.Api,
}, nil
}

View File

@@ -0,0 +1,71 @@
package query
import (
"context"
"database/sql"
"encoding/hex"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type UpdateQueryDataLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 更新查询数据
func NewUpdateQueryDataLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateQueryDataLogic {
return &UpdateQueryDataLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateQueryDataLogic) UpdateQueryData(req *types.UpdateQueryDataReq) (resp *types.UpdateQueryDataResp, err error) {
// 1. 从数据库中获取查询记录
query, err := l.svcCtx.QueryModel.FindOne(l.ctx, req.Id)
if err != nil {
if err == model.ErrNotFound {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询记录不存在, 查询ID: %d", req.Id)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询数据库失败, 查询ID: %d, err: %v", req.Id, 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. 加密数据 - 传入的是JSON需要加密处理
encryptData, aesEncryptErr := crypto.AesEncrypt([]byte(req.QueryData), key)
if aesEncryptErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密查询数据失败: %v", aesEncryptErr)
}
// 4. 更新数据库记录
query.QueryData = sql.NullString{
String: encryptData,
Valid: true,
}
updateErr := l.svcCtx.QueryModel.UpdateWithVersion(l.ctx, nil, query)
if updateErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新查询数据失败: %v", updateErr)
}
// 5. 返回结果
return &types.UpdateQueryDataResp{
Id: query.Id,
UpdatedAt: query.UpdateTime.Format("2006-01-02 15:04:05"),
}, nil
}

View File

@@ -0,0 +1,40 @@
package report
import (
"context"
"fmt"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type DownloadReportPdfLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDownloadReportPdfLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DownloadReportPdfLogic {
return &DownloadReportPdfLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DownloadReportPdfLogic) DownloadReportPdf(req *types.DownloadReportPdfReq) (resp *types.DownloadReportPdfResp, err error) {
pdfBytes, err := l.svcCtx.ReportPDFService.GenerateReportPDF(l.ctx, req.OrderId, req.OrderNo)
if err != nil {
logx.Errorf("生成报告 PDF 失败, orderId=%s, orderNo=%s, err=%v", req.OrderId, req.OrderNo, err)
return nil, fmt.Errorf("生成报告 PDF 失败: %w", err)
}
resp = &types.DownloadReportPdfResp{
FileName: fmt.Sprintf("report_%s.pdf", req.OrderNo),
Content: pdfBytes,
}
return resp, nil
}

View File

@@ -0,0 +1,140 @@
package user
import (
"context"
"time"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/xerr"
"github.com/google/uuid"
"github.com/pkg/errors"
)
type AuthLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAuthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AuthLogic {
return &AuthLogic{ctx: ctx, svcCtx: svcCtx}
}
func (l *AuthLogic) Auth(req *types.AuthReq) (*types.AuthResp, error) {
var userID string
var userType int64
var authType string
var authKey string
switch req.Platform {
case model.PlatformH5:
authType = model.UserAuthTypeUUID
authKey = uuid.NewString()
user, err := l.findOrCreateUserByAuth(authType, authKey)
if err != nil {
return nil, err
}
userID = user.Id
userType = l.getUserType(user)
case model.PlatformWxH5:
openid, err := l.svcCtx.VerificationService.GetWechatH5OpenID(l.ctx, req.Code)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取WxH5 OpenID失败: %v", err)
}
authType = model.UserAuthTypeWxh5OpenID
authKey = openid
userAuth, err := l.svcCtx.UserAuthModel.FindOneByAuthTypeAuthKey(l.ctx, authType, authKey)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户授权失败: %v", err)
}
if userAuth != nil {
user, _ := l.svcCtx.UserModel.FindOne(l.ctx, userAuth.UserId)
userID = user.Id
userType = l.getUserType(user)
} else {
user, err := l.createUserWithAuth(authType, authKey)
if err != nil {
return nil, err
}
userID = user.Id
userType = model.UserTypeTemp
}
case model.PlatformWxMini:
openid, err := l.svcCtx.VerificationService.GetWechatMiniOpenID(l.ctx, req.Code)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取WxMini OpenID失败: %v", err)
}
authType = model.UserAuthTypeWxMiniOpenID
authKey = openid
userAuth, err := l.svcCtx.UserAuthModel.FindOneByAuthTypeAuthKey(l.ctx, authType, authKey)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户授权失败: %v", err)
}
if userAuth != nil {
user, _ := l.svcCtx.UserModel.FindOne(l.ctx, userAuth.UserId)
userID = user.Id
userType = l.getUserType(user)
} else {
user, err := l.createUserWithAuth(authType, authKey)
if err != nil {
return nil, err
}
userID = user.Id
userType = model.UserTypeTemp
}
default:
return nil, errors.Wrapf(xerr.NewErrMsg("不支持的平台类型"), "platform=%s", req.Platform)
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成Token失败: %v", err)
}
now := time.Now().Unix()
user, _ := l.svcCtx.UserModel.FindOne(l.ctx, userID)
hasMobile := user.Mobile.Valid
return &types.AuthResp{
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
UserType: userType,
HasMobile: hasMobile,
// 代理系统已下线,统一返回 false
IsAgent: false,
}, nil
}
func (l *AuthLogic) findOrCreateUserByAuth(authType, authKey string) (*model.User, error) {
userAuth, err := l.svcCtx.UserAuthModel.FindOneByAuthTypeAuthKey(l.ctx, authType, authKey)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找用户授权失败: %v", err)
}
if userAuth != nil {
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userAuth.UserId)
return user, err
}
return l.createUserWithAuth(authType, authKey)
}
func (l *AuthLogic) createUserWithAuth(authType, authKey string) (*model.User, error) {
user := &model.User{Id: uuid.NewString()}
_, err := l.svcCtx.UserModel.Insert(l.ctx, nil, user)
if err != nil {
return nil, err
}
ua := &model.UserAuth{Id: uuid.NewString(), UserId: user.Id, AuthType: authType, AuthKey: authKey}
_, err = l.svcCtx.UserAuthModel.Insert(l.ctx, nil, ua)
if err != nil {
return nil, err
}
return l.svcCtx.UserModel.FindOne(l.ctx, user.Id)
}
func (l *AuthLogic) getUserType(user *model.User) int64 {
if user.Mobile.Valid {
return model.UserTypeNormal
}
return model.UserTypeTemp
}

View File

@@ -0,0 +1,260 @@
package user
import (
"context"
"database/sql"
"fmt"
"os"
"time"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/ctxdata"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type BindMobileLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewBindMobileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BindMobileLogic {
return &BindMobileLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *BindMobileLogic) BindMobile(req *types.BindMobileReq) (resp *types.BindMobileResp, err error) {
// 从上下文中获取当前登录态的用户声明可能是临时用户或正式用户包含UserId/AuthType/AuthKey
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)
}
// 当前登录用户信息(用于后续合并/绑定)
// 系统简化:未登录时创建新临时用户
var currentUserID, currentAuthType, currentAuthKey string
if claims == nil {
// 未登录,创建临时用户
tempUserID, err := l.svcCtx.UserService.RegisterUUIDUser(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建临时用户失败: %v", err)
}
// 为临时用户创建UUID认证
userAuth, findAuthErr := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, tempUserID, model.UserAuthTypeUUID)
if findAuthErr != nil && !errors.Is(findAuthErr, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询UUID认证失败: %v", findAuthErr)
}
if userAuth == nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "临时用户UUID认证不存在")
}
currentUserID = tempUserID
currentAuthType = model.UserAuthTypeUUID
currentAuthKey = userAuth.AuthKey
} else {
currentUserID = claims.UserId
currentAuthType = claims.AuthType
currentAuthKey = claims.AuthKey
}
// 加密手机号(所有手机号以密文存储)
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)
}
// 非开发环境下校验短信验证码从Redis读取并比对
if os.Getenv("ENV") != "development" {
redisKey := fmt.Sprintf("%s:%s", "bindMobile", 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("验证码不正确"), "")
}
}
// 通过加密后的手机号查找目标用户(手机号用户视为正式用户)
targetUser, err := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找用户失败: %v", err)
}
var finalUserID string
if targetUser == nil {
// 手机号不存在直接将当前用户升级为正式用户写入mobile与mobile认证
finalUserID = currentUserID
currentUser, err := l.svcCtx.UserModel.FindOne(l.ctx, currentUserID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找当前用户失败: %v", err)
}
currentUser.Mobile = sql.NullString{String: encryptedMobile, Valid: true}
if _, err := l.svcCtx.UserModel.Update(l.ctx, nil, currentUser); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新手机号失败: %v", err)
}
// 记录mobile认证确保后续可通过手机号登录
if _, err := l.svcCtx.UserAuthModel.Insert(l.ctx, nil, &model.UserAuth{Id: uuid.NewString(), UserId: finalUserID, AuthType: model.UserAuthTypeMobile, AuthKey: encryptedMobile}); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建手机号认证失败: %v", err)
}
// 发放tokenuserType会根据mobile字段动态计算
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, finalUserID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成Token失败: %v", err)
}
now := time.Now().Unix()
return &types.BindMobileResp{AccessToken: token, AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire, RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter}, nil
}
// 手机号已存在:进入账号合并或快捷登录流程
finalUserID = targetUser.Id
// 查找当前登录态使用的认证例如uuid或微信openid是否已存在
existingAuth, err := l.svcCtx.UserAuthModel.FindOneByAuthTypeAuthKey(l.ctx, currentAuthType, currentAuthKey)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找认证信息失败: %v", err)
}
// 如果当前认证已属于目标手机号用户直接发放token无需合并
if existingAuth != nil && existingAuth.UserId == finalUserID {
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, finalUserID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成Token失败: %v", err)
}
now := time.Now().Unix()
return &types.BindMobileResp{AccessToken: token, AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire, RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter}, nil
}
// 微信唯一性约束(按类型):
// - H5 与 小程序各自只能绑定一个 openid互不影响
if currentAuthType == model.UserAuthTypeWxh5OpenID {
wxh5Auth, err := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, finalUserID, model.UserAuthTypeWxh5OpenID)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找认证信息失败: %v", err)
}
if wxh5Auth != nil && wxh5Auth.AuthKey != currentAuthKey {
return nil, errors.Wrapf(xerr.NewErrMsg("该手机号已绑定其他H5微信号"), "")
}
}
if currentAuthType == model.UserAuthTypeWxMiniOpenID {
wxminiAuth, err := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, finalUserID, model.UserAuthTypeWxMiniOpenID)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找认证信息失败: %v", err)
}
if wxminiAuth != nil && wxminiAuth.AuthKey != currentAuthKey {
return nil, errors.Wrapf(xerr.NewErrMsg("该手机号已绑定其他小程序微信号"), "")
}
}
// 事务处理:
// - 将当前登录态的认证uuid / 微信openid 等绑定到目标手机号用户finalUserID
// - 将源用户currentUserID的业务数据订单、报告迁移到目标用户避免数据分裂
// - 对源用户执行软删除,清理无主临时账号,保持数据一致性
// 注意:所有步骤必须在同一个事务中执行,任何一步失败均会回滚,确保原子性
err = l.svcCtx.UserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 1) 认证绑定处理UUID替换策略
if currentAuthType == model.UserAuthTypeUUID {
targetUUIDAuth, _ := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(ctx, finalUserID, model.UserAuthTypeUUID)
if existingAuth != nil && existingAuth.UserId != finalUserID {
if targetUUIDAuth != nil {
if targetUUIDAuth.AuthKey != currentAuthKey {
if err := l.svcCtx.UserAuthModel.Delete(ctx, session, existingAuth.Id); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "删除旧UUID认证失败: %v", err)
}
targetUUIDAuth.AuthKey = currentAuthKey
if _, err := l.svcCtx.UserAuthModel.Update(ctx, session, targetUUIDAuth); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新目标UUID认证失败: %v", err)
}
} else {
if err := l.svcCtx.UserAuthModel.Delete(ctx, session, existingAuth.Id); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "删除重复UUID认证失败: %v", err)
}
}
} else {
existingAuth.UserId = finalUserID
if _, err := l.svcCtx.UserAuthModel.Update(ctx, session, existingAuth); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "迁移UUID认证失败: %v", err)
}
}
} else if existingAuth == nil {
if targetUUIDAuth != nil {
if targetUUIDAuth.AuthKey != currentAuthKey {
targetUUIDAuth.AuthKey = currentAuthKey
if _, err := l.svcCtx.UserAuthModel.Update(ctx, session, targetUUIDAuth); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新目标UUID认证失败: %v", err)
}
}
} else {
_, err := l.svcCtx.UserAuthModel.Insert(ctx, session, &model.UserAuth{Id: uuid.NewString(), UserId: finalUserID, AuthType: currentAuthType, AuthKey: currentAuthKey})
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建UUID认证失败: %v", err)
}
}
}
} else {
if existingAuth != nil && existingAuth.UserId != finalUserID {
existingAuth.UserId = finalUserID
if _, err := l.svcCtx.UserAuthModel.Update(ctx, session, existingAuth); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新认证绑定失败: %v", err)
}
} else if existingAuth == nil {
_, err := l.svcCtx.UserAuthModel.Insert(ctx, session, &model.UserAuth{Id: uuid.NewString(), UserId: finalUserID, AuthType: currentAuthType, AuthKey: currentAuthKey})
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建认证绑定失败: %v", err)
}
}
}
// 2) 业务数据迁移
// 当源用户与目标用户不同时迁移源用户的订单与报告归属到finalUserID避免合并后数据仍挂在旧用户
if currentUserID != finalUserID {
if err := l.svcCtx.OrderModel.UpdateUserIDWithSession(ctx, session, currentUserID, finalUserID); err != nil {
return err
}
if err := l.svcCtx.QueryModel.UpdateUserIDWithSession(ctx, session, currentUserID, finalUserID); err != nil {
return err
}
// 3) 源用户软删除
// 软删源用户(通常为临时用户),防止遗留无效账号;软删可保留历史痕迹,满足审计需求
currentUser, err := l.svcCtx.UserModel.FindOne(ctx, currentUserID)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找当前用户失败: %v", err)
}
if err := l.svcCtx.UserModel.Delete(ctx, session, currentUser.Id); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "删除当前用户失败: %v", err)
}
}
return nil
})
if err != nil {
return nil, err
}
// 合并完成后生成tokenuserType会根据mobile字段动态计算
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, finalUserID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成Token失败: %v", err)
}
now := time.Now().Unix()
return &types.BindMobileResp{AccessToken: token, AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire, RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter}, nil
}
// createAgentOnRegister 自动创建代理记录(注册即成为代理)
func (l *BindMobileLogic) createAgentOnRegister(ctx context.Context, userID string, encryptedMobile string) error {
// 代理系统已下线,此方法不再执行任何逻辑,保留函数签名以兼容旧调用点。
return nil
}

View File

@@ -0,0 +1,117 @@
package user
import (
"context"
"in-server/common/ctxdata"
"in-server/common/xerr"
"github.com/zeromicro/go-zero/core/mr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"in-server/app/main/api/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type CancelOutLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCancelOutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CancelOutLogic {
return &CancelOutLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CancelOutLogic) CancelOut() error {
userID, getUserIdErr := ctxdata.GetUidFromCtx(l.ctx)
if getUserIdErr != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", getUserIdErr)
}
// 在事务中处理用户注销相关操作
err := l.svcCtx.UserModel.Trans(l.ctx, func(tranCtx context.Context, session sqlx.Session) error {
// 1. 删除用户基本信息
if err := l.svcCtx.UserModel.Delete(tranCtx, session, userID); err != nil {
return errors.Wrapf(err, "删除用户基本信息失败, userId: %d", userID)
}
// 2. 查询并删除用户授权信息
UserAuthModelBuilder := l.svcCtx.UserAuthModel.SelectBuilder().Where("user_id = ?", userID)
userAuths, err := l.svcCtx.UserAuthModel.FindAll(tranCtx, UserAuthModelBuilder, "")
if err != nil && !errors.Is(err, sqlx.ErrNotFound) {
return errors.Wrapf(err, "查询用户授权信息失败, userId: %d", userID)
}
// 并发删除用户授权信息
if len(userAuths) > 0 {
funcs := make([]func() error, len(userAuths))
for i, userAuth := range userAuths {
authID := userAuth.Id
funcs[i] = func() error {
return l.svcCtx.UserAuthModel.Delete(tranCtx, session, authID)
}
}
if err := mr.Finish(funcs...); err != nil {
return errors.Wrapf(err, "删除用户授权信息失败")
}
}
// 5. 删除用户查询记录
queryBuilder := l.svcCtx.QueryModel.SelectBuilder().Where("user_id = ?", userID)
queries, err := l.svcCtx.QueryModel.FindAll(tranCtx, queryBuilder, "")
if err != nil && !errors.Is(err, sqlx.ErrNotFound) {
return errors.Wrapf(err, "查询用户查询记录失败, userId: %d", userID)
}
if len(queries) > 0 {
queryFuncs := make([]func() error, len(queries))
for i, query := range queries {
queryId := query.Id
queryFuncs[i] = func() error {
return l.svcCtx.QueryModel.Delete(tranCtx, session, queryId)
}
}
if err := mr.Finish(queryFuncs...); err != nil {
return errors.Wrapf(err, "删除用户查询记录失败")
}
}
// 6. 删除用户订单记录
orderBuilder := l.svcCtx.OrderModel.SelectBuilder().Where("user_id = ?", userID)
orders, err := l.svcCtx.OrderModel.FindAll(tranCtx, orderBuilder, "")
if err != nil && !errors.Is(err, sqlx.ErrNotFound) {
return errors.Wrapf(err, "查询用户订单记录失败, userId: %d", userID)
}
if len(orders) > 0 {
orderFuncs := make([]func() error, len(orders))
for i, order := range orders {
orderId := order.Id
orderFuncs[i] = func() error {
return l.svcCtx.OrderModel.Delete(tranCtx, session, orderId)
}
}
if err := mr.Finish(orderFuncs...); err != nil {
return errors.Wrapf(err, "删除用户订单记录失败")
}
}
return nil
})
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户注销失败%v", err)
}
return nil
}

View File

@@ -0,0 +1,66 @@
package user
import (
"context"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/app/main/model"
"in-server/common/ctxdata"
"in-server/common/xerr"
"in-server/pkg/lzkit/crypto"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type DetailLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DetailLogic {
return &DetailLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DetailLogic) Detail() (resp *types.UserInfoResp, err error) {
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)
}
userID := claims.UserId
// 无论是临时用户还是正常用户,都需要从数据库中查询用户信息
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.USER_NOT_FOUND), "用户信息, 用户不存在, %v", err)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户信息, 数据库查询用户信息失败, %v", err)
}
var userInfo types.User
err = copier.Copy(&userInfo, user)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, 用户信息结构体复制失败, %v", err)
}
if user.Mobile.Valid {
userInfo.Mobile, err = crypto.DecryptMobile(user.Mobile.String, l.svcCtx.Config.Encrypt.SecretKey)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, 解密手机号失败, %v", err)
}
}
userInfo.UserType = claims.UserType
return &types.UserInfoResp{
UserInfo: userInfo,
}, nil
}

View File

@@ -0,0 +1,191 @@
package user
import (
"context"
"crypto/sha1"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"sort"
"strings"
"time"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"in-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type GetSignatureLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetSignatureLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSignatureLogic {
return &GetSignatureLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetSignatureLogic) GetSignature(req *types.GetSignatureReq) (resp *types.GetSignatureResp, err error) {
// 检查微信公众号登录是否启用
if !l.svcCtx.Config.WechatH5.Enabled {
l.Errorf("微信公众号签名功能未启用")
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "微信公众号签名功能未启用")
}
// 1. 获取access_token
accessToken, err := l.getAccessToken()
if err != nil {
l.Errorf("获取access_token失败: %v", err)
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取access_token失败: %v", err)
}
// 2. 获取jsapi_ticket
jsapiTicket, err := l.getJsapiTicket(accessToken)
if err != nil {
l.Errorf("获取jsapi_ticket失败: %v", err)
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取jsapi_ticket失败: %v", err)
}
// 3. 生成签名
timestamp := time.Now().Unix()
nonceStr := l.generateNonceStr(16)
signature := l.generateSignature(jsapiTicket, nonceStr, timestamp, req.Url)
// 4. 返回完整的JS-SDK配置信息
return &types.GetSignatureResp{
AppId: l.svcCtx.Config.WechatH5.AppID,
Timestamp: timestamp,
NonceStr: nonceStr,
Signature: signature,
}, nil
}
// getAccessToken 获取微信公众号access_token
func (l *GetSignatureLogic) getAccessToken() (string, error) {
appID := l.svcCtx.Config.WechatH5.AppID
appSecret := l.svcCtx.Config.WechatH5.AppSecret
url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appID, appSecret)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
var result struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
if err = json.Unmarshal(body, &result); err != nil {
return "", err
}
if result.ErrCode != 0 {
return "", fmt.Errorf("获取access_token失败: errcode=%d, errmsg=%s", result.ErrCode, result.ErrMsg)
}
return result.AccessToken, nil
}
// getJsapiTicket 获取jsapi_ticket
func (l *GetSignatureLogic) getJsapiTicket(accessToken string) (string, error) {
url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi", accessToken)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
var result struct {
Ticket string `json:"ticket"`
ExpiresIn int `json:"expires_in"`
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
if err = json.Unmarshal(body, &result); err != nil {
return "", err
}
if result.ErrCode != 0 {
return "", fmt.Errorf("获取jsapi_ticket失败: errcode=%d, errmsg=%s", result.ErrCode, result.ErrMsg)
}
return result.Ticket, nil
}
// generateNonceStr 生成随机字符串
func (l *GetSignatureLogic) generateNonceStr(length int) string {
chars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
result := make([]byte, length)
for i := 0; i < length; i++ {
result[i] = chars[i%len(chars)]
}
return string(result)
}
// generateSignature 生成签名
func (l *GetSignatureLogic) generateSignature(jsapiTicket, nonceStr string, timestamp int64, urlStr string) string {
// 对URL进行解码避免重复编码
decodedURL, err := url.QueryUnescape(urlStr)
if err != nil {
decodedURL = urlStr
}
// 构建签名字符串
params := map[string]string{
"jsapi_ticket": jsapiTicket,
"noncestr": nonceStr,
"timestamp": fmt.Sprintf("%d", timestamp),
"url": decodedURL,
}
// 对参数进行字典序排序
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
// 拼接字符串
var signStr strings.Builder
for i, k := range keys {
if i > 0 {
signStr.WriteString("&")
}
signStr.WriteString(k)
signStr.WriteString("=")
signStr.WriteString(params[k])
}
// SHA1加密
h := sha1.New()
h.Write([]byte(signStr.String()))
signature := fmt.Sprintf("%x", h.Sum(nil))
return signature
}

View File

@@ -0,0 +1,47 @@
package user
import (
"context"
"time"
"in-server/common/ctxdata"
"in-server/common/xerr"
"github.com/pkg/errors"
"in-server/app/main/api/internal/svc"
"in-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetTokenLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTokenLogic {
return &GetTokenLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetTokenLogic) GetToken() (resp *types.MobileCodeLoginResp, err error) {
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, claims.UserId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)
}
// 获取当前时间戳
now := time.Now().Unix()
return &types.MobileCodeLoginResp{
AccessToken: token,
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
}, nil
}

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