fix: 修复分页字段/角色DTO/套餐详情/批量分配Bug,新增C端测试登录接口和Hurl价格验证测试
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled

- 修复B-2:27个分页DTO字段名统一(list→items, page_size→size,删除total_pages)
- 修复B-1:角色接口统一返回DTO(id小写,消除GORM大写字段名)
- 修复C-1:套餐详情接口(GET /packages/:id)补充代理佣金字段
- 修复C-2:批量分配已存在记录由500改为静默跳过
- 修复C-3:创建系列授权响应在事务提交后构建,packages数组不再为空
- 新增C端开发测试登录接口(POST /api/c/v1/auth/dev-login,仅logging.development=true时生效)
- 新增Hurl测试:card-price-verification-flow.hurl(导入卡→分配→C端价格验证)
- 新增测试Excel数据文件和dev.env测试变量
This commit is contained in:
2026-03-31 09:54:08 +08:00
parent 33e7f99fdc
commit 2ef8b8a705
54 changed files with 1697 additions and 260 deletions

View File

@@ -229,17 +229,11 @@ func (h *PollingAlertHandler) ListHistory(c *fiber.Ctx) error {
})
}
totalPages := int(total) / req.PageSize
if int(total)%req.PageSize > 0 {
totalPages++
}
return response.Success(c, &dto.PollingAlertHistoryListResp{
Items: items,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
TotalPages: totalPages,
Items: items,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
})
}

View File

@@ -214,17 +214,11 @@ func (h *PollingCleanupHandler) ListLogs(c *fiber.Ctx) error {
items = append(items, h.toLogResp(log))
}
totalPages := int(total) / req.PageSize
if int(total)%req.PageSize > 0 {
totalPages++
}
return response.Success(c, &dto.DataCleanupLogListResp{
Items: items,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
TotalPages: totalPages,
Items: items,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
})
}

View File

@@ -206,17 +206,11 @@ func (h *PollingManualTriggerHandler) ListHistory(c *fiber.Ctx) error {
items = append(items, h.toLogResp(log))
}
totalPages := int(total) / req.PageSize
if int(total)%req.PageSize > 0 {
totalPages++
}
return response.Success(c, &dto.ManualTriggerLogListResp{
Items: items,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
TotalPages: totalPages,
Items: items,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
})
}

View File

@@ -149,6 +149,29 @@ func (h *ClientAuthHandler) ChangePhone(c *fiber.Ctx) error {
return response.Success(c, resp)
}
// DevLogin 开发环境测试登录(仅开发模式可用)
// POST /api/c/v1/auth/dev-login
func (h *ClientAuthHandler) DevLogin(c *fiber.Ctx) error {
var req dto.DevLoginRequest
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam)
}
if err := clientAuthValidator.Struct(&req); err != nil {
h.logger.Warn("测试登录参数校验失败", zap.Error(err))
return errors.New(errors.CodeInvalidParam)
}
token, customerID, isNewUser, err := h.service.DevLogin(c.UserContext(), req.Identifier)
if err != nil {
return err
}
return response.Success(c, dto.DevLoginResponse{
Token: token,
CustomerID: customerID,
IsNewUser: isNewUser,
})
}
// Logout A7 退出登录
// POST /api/c/v1/auth/logout
func (h *ClientAuthHandler) Logout(c *fiber.Ctx) error {

View File

@@ -51,6 +51,6 @@ type AgentRechargeListRequest struct {
type AgentRechargeListResponse struct {
Total int64 `json:"total" description:"总记录数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"page_size" description:"每页条数"`
List []*AgentRechargeResponse `json:"list" description:"充值记录列表"`
PageSize int `json:"size" description:"每页条数"`
List []*AgentRechargeResponse `json:"items" description:"充值记录列表"`
}

View File

@@ -14,5 +14,5 @@ type AllocationConfigResponse struct {
// AllocationConfigListResponse 配置版本列表响应
type AllocationConfigListResponse struct {
List []*AllocationConfigResponse `json:"list" description:"配置版本列表"`
List []*AllocationConfigResponse `json:"items" description:"配置版本列表"`
}

View File

@@ -22,9 +22,8 @@ type PriceHistoryListRequest struct {
// PriceHistoryPageResult 成本价历史分页结果
type PriceHistoryPageResult struct {
List []*PriceHistoryResponse `json:"list" description:"历史记录列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*PriceHistoryResponse `json:"items" description:"历史记录列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}

View File

@@ -43,11 +43,10 @@ type AssetAllocationRecordResponse struct {
// ListAssetAllocationRecordResponse 分配记录列表响应
type ListAssetAllocationRecordResponse struct {
List []*AssetAllocationRecordResponse `json:"list" description:"分配记录列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*AssetAllocationRecordResponse `json:"items" description:"分配记录列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"size" description:"每页数量"`
}
// GetAssetAllocationRecordRequest 获取分配记录详情请求

View File

@@ -44,9 +44,8 @@ type AssetWalletTransactionItem struct {
// AssetWalletTransactionListResponse 资产钱包流水列表响应
type AssetWalletTransactionListResponse struct {
List []*AssetWalletTransactionItem `json:"list" description:"流水列表"`
Total int64 `json:"total" description:"总记录数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*AssetWalletTransactionItem `json:"items" description:"流水列表"`
Total int64 `json:"total" description:"总记录数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"size" description:"每页数量"`
}

View File

@@ -55,9 +55,8 @@ type UpdateCarrierStatusParams struct {
}
type CarrierPageResult struct {
List []*CarrierResponse `json:"list" description:"运营商列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*CarrierResponse `json:"items" description:"运营商列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}

View File

@@ -161,10 +161,10 @@ type AssetPackageHistoryRequest struct {
// AssetPackageHistoryResponse B3 资产套餐历史响应
type AssetPackageHistoryResponse struct {
List []AssetPackageResponse `json:"list" description:"套餐历史列表"`
List []AssetPackageResponse `json:"items" description:"套餐历史列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"页码"`
PageSize int `json:"page_size" description:"每页数量"`
PageSize int `json:"size" description:"每页数量"`
}
// ========================================

View File

@@ -101,3 +101,20 @@ type ChangePhoneResponse struct {
type LogoutResponse struct {
Success bool `json:"success" description:"是否成功"`
}
// ========================================
// DevLogin 开发环境测试登录(仅开发模式可用)
// ========================================
// DevLoginRequest 开发环境测试登录请求
// ⚠️ 仅限 logging.development=true 时生效,严禁在生产环境暴露
type DevLoginRequest struct {
Identifier string `json:"identifier" validate:"required,min=1,max=50" required:"true" minLength:"1" maxLength:"50" description:"资产标识符ICCID/虚拟号等)"`
}
// DevLoginResponse 开发环境测试登录响应
type DevLoginResponse struct {
Token string `json:"token" description:"JWT 令牌"`
CustomerID uint `json:"customer_id" description:"客户ID"`
IsNewUser bool `json:"is_new_user" description:"是否新创建的测试客户"`
}

View File

@@ -80,10 +80,10 @@ type ClientOrderListItem struct {
// ClientOrderListResponse D2 客户端订单列表响应
type ClientOrderListResponse struct {
List []ClientOrderListItem `json:"list" description:"订单列表"`
List []ClientOrderListItem `json:"items" description:"订单列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"页码"`
PageSize int `json:"page_size" description:"每页数量"`
PageSize int `json:"size" description:"每页数量"`
}
// ========================================

View File

@@ -45,10 +45,10 @@ type WalletTransactionItem struct {
// WalletTransactionListResponse C2 钱包流水列表响应
type WalletTransactionListResponse struct {
List []WalletTransactionItem `json:"list" description:"流水列表"`
List []WalletTransactionItem `json:"items" description:"流水列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"页码"`
PageSize int `json:"page_size" description:"每页数量"`
PageSize int `json:"size" description:"每页数量"`
}
// ========================================
@@ -131,8 +131,8 @@ type ClientRechargeListItem struct {
// ClientRechargeListResponse C5 充值记录列表响应
type ClientRechargeListResponse struct {
List []ClientRechargeListItem `json:"list" description:"充值记录列表"`
List []ClientRechargeListItem `json:"items" description:"充值记录列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"页码"`
PageSize int `json:"page_size" description:"每页数量"`
PageSize int `json:"size" description:"每页数量"`
}

View File

@@ -33,11 +33,10 @@ type CommissionRecordListRequest struct {
// CommissionRecordPageResult 佣金记录分页结果
type CommissionRecordPageResult struct {
List []*CommissionRecordResponse `json:"list" description:"佣金记录列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*CommissionRecordResponse `json:"items" description:"佣金记录列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}
// CommissionStatsResponse 佣金统计响应

View File

@@ -49,11 +49,10 @@ type DeviceResponse struct {
}
type ListDeviceResponse struct {
List []*DeviceResponse `json:"list" description:"设备列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*DeviceResponse `json:"items" description:"设备列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"size" description:"每页数量"`
}
type GetDeviceRequest struct {

View File

@@ -41,11 +41,10 @@ type DeviceImportTaskResponse struct {
}
type ListDeviceImportTaskResponse struct {
List []*DeviceImportTaskResponse `json:"list" description:"任务列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*DeviceImportTaskResponse `json:"items" description:"任务列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"size" description:"每页数量"`
}
type DeviceImportResultItemDTO struct {

View File

@@ -51,7 +51,7 @@ type H5EnterpriseDeviceListReq struct {
}
type EnterpriseDeviceListResp struct {
List []EnterpriseDeviceItem `json:"list" description:"设备列表"`
List []EnterpriseDeviceItem `json:"items" description:"设备列表"`
Total int64 `json:"total" description:"总数"`
}

View File

@@ -88,10 +88,10 @@ type ExchangeOrderResponse struct {
}
type ExchangeListResponse struct {
List []*ExchangeOrderResponse `json:"list" description:"换货单列表"`
List []*ExchangeOrderResponse `json:"items" description:"换货单列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"page_size" description:"每页数量"`
PageSize int `json:"size" description:"每页数量"`
}
type ClientExchangePendingResponse struct {

View File

@@ -55,11 +55,10 @@ type StandaloneIotCardResponse struct {
}
type ListStandaloneIotCardResponse struct {
List []*StandaloneIotCardResponse `json:"list" description:"单卡列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*StandaloneIotCardResponse `json:"items" description:"单卡列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"size" description:"每页数量"`
}
type ImportIotCardRequest struct {
@@ -105,11 +104,10 @@ type ImportTaskResponse struct {
}
type ListImportTaskResponse struct {
List []*ImportTaskResponse `json:"list" description:"任务列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*ImportTaskResponse `json:"items" description:"任务列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"size" description:"每页数量"`
}
type ImportResultItemDTO struct {

View File

@@ -26,11 +26,10 @@ type MyPackageResponse struct {
// MyPackagePageResult 我的可售套餐分页结果
type MyPackagePageResult struct {
List []*MyPackageResponse `json:"list" description:"套餐列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*MyPackageResponse `json:"items" description:"套餐列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}
// MyPackageDetailResponse 我的可售套餐详情响应
@@ -71,11 +70,10 @@ type MySeriesAllocationResponse struct {
// MySeriesAllocationPageResult 我的套餐系列分配分页结果
type MySeriesAllocationPageResult struct {
List []*MySeriesAllocationResponse `json:"list" description:"分配列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*MySeriesAllocationResponse `json:"items" description:"分配列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}
// PriceSource 价格来源常量

View File

@@ -82,11 +82,10 @@ type OrderResponse struct {
}
type OrderListResponse struct {
List []*OrderResponse `json:"list" description:"订单列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*OrderResponse `json:"items" description:"订单列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"size" description:"每页数量"`
}
type GetOrderRequest struct {

View File

@@ -124,11 +124,10 @@ type UpdateRetailPriceParams struct {
// PackagePageResult 套餐分页结果
type PackagePageResult struct {
List []*PackageResponse `json:"list" description:"套餐列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*PackageResponse `json:"items" description:"套餐列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}
// PackageUsageItemResponse 套餐使用项响应(客户视图)

View File

@@ -82,9 +82,8 @@ type UpdatePackageSeriesStatusParams struct {
// PackageSeriesPageResult 套餐系列分页结果
type PackageSeriesPageResult struct {
List []*PackageSeriesResponse `json:"list" description:"套餐系列列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*PackageSeriesResponse `json:"items" description:"套餐系列列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}

View File

@@ -69,11 +69,10 @@ type PollingAlertHistoryResp struct {
// PollingAlertHistoryListResp 告警历史列表响应
type PollingAlertHistoryListResp struct {
Items []*PollingAlertHistoryResp `json:"items" description:"告警历史列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
Items []*PollingAlertHistoryResp `json:"items" description:"告警历史列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}
// ListPollingAlertHistoryReq 查询告警历史请求

View File

@@ -59,11 +59,10 @@ type DataCleanupLogResp struct {
// DataCleanupLogListResp 数据清理日志列表响应
type DataCleanupLogListResp struct {
Items []*DataCleanupLogResp `json:"items" description:"日志列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
Items []*DataCleanupLogResp `json:"items" description:"日志列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}
// ListDataCleanupLogReq 查询数据清理日志请求

View File

@@ -73,9 +73,8 @@ type UpdatePollingConfigStatusParams struct {
// PollingConfigPageResult 轮询配置分页结果
type PollingConfigPageResult struct {
List []*PollingConfigResponse `json:"list" description:"配置列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*PollingConfigResponse `json:"items" description:"配置列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}

View File

@@ -51,11 +51,10 @@ type ManualTriggerLogResp struct {
// ManualTriggerLogListResp 手动触发日志列表响应
type ManualTriggerLogListResp struct {
Items []*ManualTriggerLogResp `json:"items" description:"日志列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
Items []*ManualTriggerLogResp `json:"items" description:"日志列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}
// ListManualTriggerLogReq 查询手动触发日志请求

View File

@@ -64,16 +64,10 @@ type RechargeListRequest struct {
// RechargeListResponse 充值订单列表响应
type RechargeListResponse struct {
// 列表数据
List []*RechargeResponse `json:"list" description:"列表数据"`
// 总记录数
Total int64 `json:"total" description:"总记录数"`
// 当前页码
Page int `json:"page" description:"当前页码"`
// 每页数量
PageSize int `json:"page_size" description:"每页数量"`
// 总页数
TotalPages int `json:"total_pages" description:"总页数"`
List []*RechargeResponse `json:"items" description:"列表数据"`
Total int64 `json:"total" description:"总记录数"`
Page int `json:"page" description:"当前页码"`
PageSize int `json:"size" description:"每页数量"`
}
// RechargeCheckRequest 充值预检请求

View File

@@ -103,11 +103,10 @@ type ShopSeriesGrantListItem struct {
// ShopSeriesGrantPageResult 系列授权分页结果
type ShopSeriesGrantPageResult struct {
List []*ShopSeriesGrantListItem `json:"list" description:"授权列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
TotalPages int `json:"total_pages" description:"总页数"`
List []*ShopSeriesGrantListItem `json:"items" description:"授权列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"size" description:"每页数量"`
}
// UpdateShopSeriesGrantParams 更新系列授权聚合参数(文档生成用)

View File

@@ -123,10 +123,10 @@ type WechatConfigResponse struct {
// WechatConfigListResponse 微信参数配置列表响应
type WechatConfigListResponse struct {
List []*WechatConfigResponse `json:"list" description:"配置列表"`
List []*WechatConfigResponse `json:"items" description:"配置列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
PageSize int `json:"size" description:"每页数量"`
}
// MaskShortSecret 对短密钥进行脱敏处理

View File

@@ -7,6 +7,7 @@ import (
apphandler "github.com/break/junhong_cmp_fiber/internal/handler/app"
"github.com/break/junhong_cmp_fiber/internal/middleware"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/pkg/config"
"github.com/break/junhong_cmp_fiber/pkg/openapi"
)
@@ -53,6 +54,18 @@ func RegisterPersonalCustomerRoutes(router fiber.Router, doc *openapi.Generator,
Output: &dto.ClientSendCodeResponse{},
})
// 开发模式测试登录接口(仅 logging.development=true 时注册)
if config.Get().Logging.Development {
Register(router, doc, basePath, "POST", authBasePath+"/dev-login", handlers.ClientAuth.DevLogin, RouteSpec{
Summary: "开发环境测试登录",
Description: "⚠️ 仅开发模式可用。传入资产标识符ICCID等自动绑定测试客户并签发 JWT用于接口测试。",
Tags: []string{"个人客户 - 认证"},
Auth: false,
Input: &dto.DevLoginRequest{},
Output: &dto.DevLoginResponse{},
})
}
// === 需要认证的 auth 路由 ===
authProtectedGroup := router.Group(authBasePath)
authProtectedGroup.Use(personalAuthMiddleware.Authenticate())

View File

@@ -101,17 +101,11 @@ func (s *Service) List(ctx context.Context, req *dto.ListAssetAllocationRecordRe
list = append(list, item)
}
totalPages := int(total) / pageSize
if int(total)%pageSize > 0 {
totalPages++
}
return &dto.ListAssetAllocationRecordResponse{
List: list,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: totalPages,
List: list,
Total: total,
Page: page,
PageSize: pageSize,
}, nil
}

View File

@@ -108,17 +108,11 @@ func (s *Service) ListTransactions(ctx context.Context, assetType string, assetI
})
}
totalPages := int(total) / pageSize
if int(total)%pageSize > 0 {
totalPages++
}
return &dto.AssetWalletTransactionListResponse{
List: list,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: totalPages,
List: list,
Total: total,
Page: page,
PageSize: pageSize,
}, nil
}

View File

@@ -717,6 +717,54 @@ func (s *Service) issueLoginToken(ctx context.Context, customerID uint) (string,
return token, needBindPhone, nil
}
// DevLogin 开发环境测试登录
// 根据资产标识符查找或创建测试客户并直接签发 JWT无需微信 OAuth
// ⚠️ 仅限 logging.development=true 时由路由层暴露,严禁生产环境调用
func (s *Service) DevLogin(ctx context.Context, identifier string) (string, uint, bool, error) {
assetType, assetID, err := s.resolveAsset(ctx, identifier)
if err != nil {
return "", 0, false, err
}
var (
customerID uint
isNewUser bool
)
// 在事务中查找或创建测试客户并绑定资产
// 使用固定的开发测试 OpenIDdev_test_+identifier避免重复创建
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
devOpenID := "dev_test_" + identifier
devAppID := "dev_test_app"
cid, created, findErr := s.findOrCreateCustomer(ctx, tx, devAppID, devOpenID, "", "测试用户", "", "dev")
if findErr != nil {
return findErr
}
if bindErr := s.bindAsset(ctx, tx, cid, assetType, assetID); bindErr != nil {
return bindErr
}
customerID = cid
isNewUser = created
return nil
})
if err != nil {
return "", 0, false, err
}
token, _, err := s.issueLoginToken(ctx, customerID)
if err != nil {
return "", 0, false, err
}
s.logger.Info("开发环境测试登录成功",
zap.Uint("customer_id", customerID),
zap.String("identifier", identifier),
)
return token, customerID, isNewUser, nil
}
func (s *Service) checkSendCodeRateLimit(ctx context.Context, phone, clientIP string) error {
phoneCooldownKey := constants.RedisClientSendCodePhoneLimitKey(phone)
exists, err := s.redis.Exists(ctx, phoneCooldownKey).Result()

View File

@@ -127,17 +127,11 @@ func (s *Service) List(ctx context.Context, req *dto.ListDeviceRequest) (*dto.Li
list = append(list, item)
}
totalPages := int(total) / pageSize
if int(total)%pageSize > 0 {
totalPages++
}
return &dto.ListDeviceResponse{
List: list,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: totalPages,
List: list,
Total: total,
Page: page,
PageSize: pageSize,
}, nil
}

View File

@@ -110,17 +110,11 @@ func (s *Service) List(ctx context.Context, req *dto.ListDeviceImportTaskRequest
list = append(list, s.toTaskResponse(task))
}
totalPages := int(total) / pageSize
if int(total)%pageSize > 0 {
totalPages++
}
return &dto.ListDeviceImportTaskResponse{
List: list,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: totalPages,
List: list,
Total: total,
Page: page,
PageSize: pageSize,
}, nil
}

View File

@@ -157,17 +157,11 @@ func (s *Service) ListStandalone(ctx context.Context, req *dto.ListStandaloneIot
list = append(list, item)
}
totalPages := int(total) / pageSize
if int(total)%pageSize > 0 {
totalPages++
}
return &dto.ListStandaloneIotCardResponse{
List: list,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: totalPages,
List: list,
Total: total,
Page: page,
PageSize: pageSize,
}, nil
}

View File

@@ -143,17 +143,11 @@ func (s *Service) List(ctx context.Context, req *dto.ListImportTaskRequest) (*dt
list = append(list, s.toTaskResponse(task))
}
totalPages := int(total) / pageSize
if int(total)%pageSize > 0 {
totalPages++
}
return &dto.ListImportTaskResponse{
List: list,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: totalPages,
List: list,
Total: total,
Page: page,
PageSize: pageSize,
}, nil
}

View File

@@ -1234,17 +1234,11 @@ func (s *Service) List(ctx context.Context, req *dto.OrderListRequest, buyerType
list = append(list, s.buildOrderResponse(o, itemsMap[o.ID]))
}
totalPages := int(total) / pageSize
if int(total)%pageSize > 0 {
totalPages++
}
return &dto.OrderListResponse{
List: list,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: totalPages,
List: list,
Total: total,
Page: page,
PageSize: pageSize,
}, nil
}

View File

@@ -155,6 +155,28 @@ func (s *Service) Get(ctx context.Context, id uint) (*dto.PackageResponse, error
series, err := s.packageSeriesStore.GetByID(ctx, pkg.SeriesID)
if err == nil {
resp.SeriesName = &series.SeriesName
// 代理用户:增强佣金信息(与 List 接口保持一致)
userType := middleware.GetUserTypeFromContext(ctx)
shopID := middleware.GetShopIDFromContext(ctx)
if userType == constants.UserTypeAgent && shopID > 0 && series.EnableOneTimeCommission {
config, _ := series.GetOneTimeCommissionConfig()
if config != nil && config.Enable {
allocations, saErr := s.shopSeriesAllocationStore.GetByShopID(ctx, shopID)
if saErr == nil {
seriesAllocationMap := make(map[uint]*model.ShopSeriesAllocation)
seriesConfigMap := make(map[uint]*model.OneTimeCommissionConfig)
for _, alloc := range allocations {
if alloc.SeriesID == pkg.SeriesID {
seriesAllocationMap[alloc.SeriesID] = alloc
break
}
}
seriesConfigMap[pkg.SeriesID] = config
s.fillCommissionInfo(resp, pkg.SeriesID, seriesAllocationMap, seriesConfigMap)
}
}
}
}
}
return resp, nil

View File

@@ -258,17 +258,11 @@ func (s *Service) List(ctx context.Context, req *dto.RechargeListRequest, userID
list = append(list, s.buildRechargeResponse(r))
}
totalPages := int(total) / pageSize
if int(total)%pageSize > 0 {
totalPages++
}
return &dto.RechargeListResponse{
List: list,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: totalPages,
List: list,
Total: total,
Page: page,
PageSize: pageSize,
}, nil
}

View File

@@ -6,6 +6,7 @@ import (
"context"
"fmt"
"strings"
"time"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
@@ -34,7 +35,7 @@ func New(roleStore *postgres.RoleStore, permissionStore *postgres.PermissionStor
}
// Create 创建角色
func (s *Service) Create(ctx context.Context, req *dto.CreateRoleRequest) (*model.Role, error) {
func (s *Service) Create(ctx context.Context, req *dto.CreateRoleRequest) (*dto.RoleResponse, error) {
// 获取当前用户 ID
currentUserID := middleware.GetUserIDFromContext(ctx)
if currentUserID == 0 {
@@ -62,11 +63,11 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateRoleRequest) (*mode
return nil, errors.Wrap(errors.CodeInternalError, err, "创建角色失败")
}
return role, nil
return toResponse(role), nil
}
// Get 获取角色
func (s *Service) Get(ctx context.Context, id uint) (*model.Role, error) {
func (s *Service) Get(ctx context.Context, id uint) (*dto.RoleResponse, error) {
role, err := s.roleStore.GetByID(ctx, id)
if err != nil {
if err == gorm.ErrRecordNotFound {
@@ -74,11 +75,11 @@ func (s *Service) Get(ctx context.Context, id uint) (*model.Role, error) {
}
return nil, errors.Wrap(errors.CodeInternalError, err, "获取角色失败")
}
return role, nil
return toResponse(role), nil
}
// Update 更新角色
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateRoleRequest) (*model.Role, error) {
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateRoleRequest) (*dto.RoleResponse, error) {
// 获取当前用户 ID
currentUserID := middleware.GetUserIDFromContext(ctx)
if currentUserID == 0 {
@@ -120,7 +121,7 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateRoleReques
return nil, errors.Wrap(errors.CodeInternalError, err, "更新角色失败")
}
return role, nil
return toResponse(role), nil
}
// Delete 软删除角色
@@ -293,6 +294,21 @@ func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
return nil
}
// toResponse 将 model.Role 转换为 dto.RoleResponse
func toResponse(role *model.Role) *dto.RoleResponse {
return &dto.RoleResponse{
ID: role.ID,
RoleName: role.RoleName,
RoleDesc: role.RoleDesc,
RoleType: role.RoleType,
Status: role.Status,
Creator: role.Creator,
Updater: role.Updater,
CreatedAt: role.CreatedAt.Format(time.RFC3339),
UpdatedAt: role.UpdatedAt.Format(time.RFC3339),
}
}
func contains(availableForRoleTypes, roleTypeStr string) bool {
types := strings.Split(availableForRoleTypes, ",")
for _, t := range types {

View File

@@ -82,8 +82,15 @@ func (s *Service) BatchAllocate(ctx context.Context, req *dto.BatchAllocatePacka
}
return s.db.Transaction(func(tx *gorm.DB) error {
packageAllocations := make([]*model.ShopPackageAllocation, 0, len(packages))
txPkgAllocStore := postgres.NewShopPackageAllocationStore(tx)
for _, pkg := range packages {
// 已存在该套餐的分配记录则跳过,避免唯一约束冲突
_, existErr := txPkgAllocStore.GetByShopAndPackageForSystem(ctx, req.ShopID, pkg.ID)
if existErr == nil {
continue
}
costPrice := pkg.CostPrice
if req.PriceAdjustment != nil {
costPrice = s.calculateAdjustedPrice(pkg.CostPrice, req.PriceAdjustment)
@@ -99,11 +106,9 @@ func (s *Service) BatchAllocate(ctx context.Context, req *dto.BatchAllocatePacka
SeriesAllocationID: &seriesAllocation.ID,
Status: constants.StatusEnabled,
}
packageAllocations = append(packageAllocations, allocation)
}
if err := tx.CreateInBatches(packageAllocations, 100).Error; err != nil {
return errors.Wrap(errors.CodeInternalError, err, "批量创建套餐分配失败")
if err := tx.Create(allocation).Error; err != nil {
return errors.Wrap(errors.CodeInternalError, err, "创建套餐分配失败")
}
}
return nil

View File

@@ -212,6 +212,12 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateShopSeriesGrantRequ
return nil, errors.New(errors.CodeInvalidParam, "该系列未启用一次性佣金,无法创建授权")
}
// 1.5 校验目标店铺是否存在
_, err = s.shopStore.GetByID(ctx, req.ShopID)
if err != nil {
return nil, errors.New(errors.CodeNotFound, "目标店铺不存在")
}
// 2. 检查重复授权
exists, err := s.shopSeriesAllocationStore.ExistsByShopAndSeries(ctx, req.ShopID, req.SeriesID)
if err != nil {
@@ -300,7 +306,6 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateShopSeriesGrantRequ
}
// 6. 事务中创建 ShopSeriesAllocation + N 条 ShopPackageAllocation
var result *dto.ShopSeriesGrantResponse
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
txSeriesStore := postgres.NewShopSeriesAllocationStore(tx)
if createErr := txSeriesStore.Create(ctx, allocation); createErr != nil {
@@ -353,15 +358,14 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateShopSeriesGrantRequ
}
}
var buildErr error
result, buildErr = s.buildGrantResponse(ctx, allocation, series, config)
return buildErr
return nil
})
if err != nil {
return nil, err
}
return result, nil
// 事务提交后构建完整响应(此时 packages 已可查询到)
return s.buildGrantResponse(ctx, allocation, series, config)
}
// Get 查询单条系列授权详情
@@ -494,17 +498,11 @@ func (s *Service) List(ctx context.Context, req *dto.ShopSeriesGrantListRequest)
items = append(items, item)
}
totalPages := int(total) / pageSize
if int(total)%pageSize != 0 {
totalPages++
}
return &dto.ShopSeriesGrantPageResult{
List: items,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: totalPages,
List: items,
Total: total,
Page: page,
PageSize: pageSize,
}, nil
}