feat: 客户端接口数据模型基础准备
- 新增资产状态、订单来源、操作人类型、实名链接类型常量 - 8个模型新增字段(asset_status/generation/source/retail_price等) - 数据库迁移000082:7张表15+字段,含存量retail_price回填 - BUG-1修复:代理零售价渠道隔离,cost_price分配锁定 - BUG-2修复:一次性佣金仅客户端订单触发 - BUG-4修复:充值回调Store操作纳入事务 - 新增资产手动停用接口(PATCH /iot-cards/:id/deactivate、/devices/:id/deactivate) - Carrier管理新增实名链接配置 - 后台订单generation写时快照 - BatchUpdatePricing支持retail_price调价目标 - 清理全部H5旧接口和个人客户旧登录方法
This commit is contained in:
59
internal/handler/admin/asset_lifecycle.go
Normal file
59
internal/handler/admin/asset_lifecycle.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
)
|
||||
|
||||
// AssetLifecycleService 资产生命周期服务接口
|
||||
type AssetLifecycleService interface {
|
||||
// DeactivateIotCard 停用 IoT 卡
|
||||
DeactivateIotCard(ctx context.Context, id uint) error
|
||||
// DeactivateDevice 停用设备
|
||||
DeactivateDevice(ctx context.Context, id uint) error
|
||||
}
|
||||
|
||||
// AssetLifecycleHandler 资产生命周期处理器
|
||||
type AssetLifecycleHandler struct {
|
||||
service AssetLifecycleService
|
||||
}
|
||||
|
||||
// NewAssetLifecycleHandler 创建资产生命周期处理器
|
||||
func NewAssetLifecycleHandler(service AssetLifecycleService) *AssetLifecycleHandler {
|
||||
return &AssetLifecycleHandler{service: service}
|
||||
}
|
||||
|
||||
// DeactivateIotCard 手动停用 IoT 卡
|
||||
// PATCH /api/admin/iot-cards/:id/deactivate
|
||||
func (h *AssetLifecycleHandler) DeactivateIotCard(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "无效的卡ID")
|
||||
}
|
||||
|
||||
if err := h.service.DeactivateIotCard(c.UserContext(), uint(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, nil)
|
||||
}
|
||||
|
||||
// DeactivateDevice 手动停用设备
|
||||
// PATCH /api/admin/devices/:id/deactivate
|
||||
func (h *AssetLifecycleHandler) DeactivateDevice(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "无效的设备ID")
|
||||
}
|
||||
|
||||
if err := h.service.DeactivateDevice(c.UserContext(), uint(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, nil)
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
"github.com/break/junhong_cmp_fiber/internal/service/personal_customer"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
@@ -25,45 +24,6 @@ func NewPersonalCustomerHandler(service *personal_customer.Service, logger *zap.
|
||||
}
|
||||
}
|
||||
|
||||
// SendCodeRequest 发送验证码请求
|
||||
type SendCodeRequest struct {
|
||||
Phone string `json:"phone" validate:"required,len=11"` // 手机号(11位)
|
||||
}
|
||||
|
||||
// SendCode 发送验证码
|
||||
// POST /api/c/v1/login/send-code
|
||||
func (h *PersonalCustomerHandler) SendCode(c *fiber.Ctx) error {
|
||||
var req SendCodeRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
// 发送验证码
|
||||
if err := h.service.SendVerificationCode(c.Context(), req.Phone); err != nil {
|
||||
h.logger.Error("发送验证码失败",
|
||||
zap.String("phone", req.Phone),
|
||||
zap.Error(err),
|
||||
)
|
||||
return errors.Wrap(errors.CodeInternalError, err, "发送验证码失败")
|
||||
}
|
||||
|
||||
return response.Success(c, fiber.Map{
|
||||
"message": "验证码已发送",
|
||||
})
|
||||
}
|
||||
|
||||
// LoginRequest 登录请求
|
||||
type LoginRequest struct {
|
||||
Phone string `json:"phone" validate:"required,len=11"` // 手机号(11位)
|
||||
Code string `json:"code" validate:"required,len=6"` // 验证码(6位)
|
||||
}
|
||||
|
||||
// LoginResponse 登录响应
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"` // 访问令牌
|
||||
Customer *PersonalCustomerDTO `json:"customer"` // 客户信息
|
||||
}
|
||||
|
||||
// PersonalCustomerDTO 个人客户 DTO
|
||||
type PersonalCustomerDTO struct {
|
||||
ID uint `json:"id"`
|
||||
@@ -74,87 +34,6 @@ type PersonalCustomerDTO struct {
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
// Login 登录(手机号 + 验证码)
|
||||
// POST /api/c/v1/login
|
||||
func (h *PersonalCustomerHandler) Login(c *fiber.Ctx) error {
|
||||
var req LoginRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
// 登录
|
||||
token, customer, err := h.service.LoginByPhone(c.Context(), req.Phone, req.Code)
|
||||
if err != nil {
|
||||
h.logger.Error("登录失败",
|
||||
zap.String("phone", req.Phone),
|
||||
zap.Error(err),
|
||||
)
|
||||
return errors.Wrap(errors.CodeInternalError, err, "登录失败")
|
||||
}
|
||||
|
||||
// 构造响应
|
||||
// 注意:Phone 字段已从 PersonalCustomer 模型移除,需要从 PersonalCustomerPhone 表查询
|
||||
resp := &LoginResponse{
|
||||
Token: token,
|
||||
Customer: &PersonalCustomerDTO{
|
||||
ID: customer.ID,
|
||||
Phone: req.Phone, // 使用请求中的手机号(临时方案)
|
||||
Nickname: customer.Nickname,
|
||||
AvatarURL: customer.AvatarURL,
|
||||
WxOpenID: customer.WxOpenID,
|
||||
Status: customer.Status,
|
||||
},
|
||||
}
|
||||
|
||||
return response.Success(c, resp)
|
||||
}
|
||||
|
||||
// WechatOAuthLogin 微信 OAuth 登录
|
||||
// POST /api/c/v1/wechat/auth
|
||||
func (h *PersonalCustomerHandler) WechatOAuthLogin(c *fiber.Ctx) error {
|
||||
var req dto.WechatOAuthRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
result, err := h.service.WechatOAuthLogin(c.Context(), req.Code)
|
||||
if err != nil {
|
||||
h.logger.Error("微信 OAuth 登录失败",
|
||||
zap.String("code", req.Code),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// BindWechat 绑定微信
|
||||
// POST /api/c/v1/bind-wechat
|
||||
func (h *PersonalCustomerHandler) BindWechat(c *fiber.Ctx) error {
|
||||
var req dto.WechatOAuthRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
customerID, ok := c.Locals("customer_id").(uint)
|
||||
if !ok {
|
||||
return errors.New(errors.CodeUnauthorized, "未找到客户信息")
|
||||
}
|
||||
|
||||
if err := h.service.BindWechatWithCode(c.Context(), customerID, req.Code); err != nil {
|
||||
h.logger.Error("绑定微信失败",
|
||||
zap.Uint("customer_id", customerID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, fiber.Map{
|
||||
"message": "绑定成功",
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateProfileRequest 更新个人资料请求
|
||||
type UpdateProfileRequest struct {
|
||||
Nickname string `json:"nickname"` // 昵称
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
package h5
|
||||
|
||||
import (
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
"github.com/break/junhong_cmp_fiber/internal/service/auth"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/logger"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// AuthHandler H5认证处理器
|
||||
type AuthHandler struct {
|
||||
authService *auth.Service
|
||||
validator *validator.Validate
|
||||
}
|
||||
|
||||
// NewAuthHandler 创建H5认证处理器
|
||||
func NewAuthHandler(authService *auth.Service, validator *validator.Validate) *AuthHandler {
|
||||
return &AuthHandler{
|
||||
authService: authService,
|
||||
validator: validator,
|
||||
}
|
||||
}
|
||||
|
||||
// Login H5登录
|
||||
func (h *AuthHandler) Login(c *fiber.Ctx) error {
|
||||
var req dto.LoginRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
if err := h.validator.Struct(&req); err != nil {
|
||||
logger.GetAppLogger().Warn("参数验证失败",
|
||||
zap.String("path", c.Path()),
|
||||
zap.String("method", c.Method()),
|
||||
zap.Error(err),
|
||||
)
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
|
||||
clientIP := c.IP()
|
||||
ctx := c.UserContext()
|
||||
|
||||
resp, err := h.authService.Login(ctx, &req, clientIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, resp)
|
||||
}
|
||||
|
||||
// Logout H5登出
|
||||
func (h *AuthHandler) Logout(c *fiber.Ctx) error {
|
||||
auth := c.Get("Authorization")
|
||||
accessToken := ""
|
||||
if len(auth) > 7 && auth[:7] == "Bearer " {
|
||||
accessToken = auth[7:]
|
||||
}
|
||||
|
||||
refreshToken := ""
|
||||
var req dto.RefreshTokenRequest
|
||||
if err := c.BodyParser(&req); err == nil {
|
||||
refreshToken = req.RefreshToken
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
|
||||
if err := h.authService.Logout(ctx, accessToken, refreshToken); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, nil)
|
||||
}
|
||||
|
||||
// RefreshToken 刷新访问令牌
|
||||
func (h *AuthHandler) RefreshToken(c *fiber.Ctx) error {
|
||||
var req dto.RefreshTokenRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
if err := h.validator.Struct(&req); err != nil {
|
||||
logger.GetAppLogger().Warn("参数验证失败",
|
||||
zap.String("path", c.Path()),
|
||||
zap.String("method", c.Method()),
|
||||
zap.Error(err),
|
||||
)
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
|
||||
newAccessToken, err := h.authService.RefreshToken(ctx, req.RefreshToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp := &dto.RefreshTokenResponse{
|
||||
AccessToken: newAccessToken,
|
||||
ExpiresIn: 86400,
|
||||
}
|
||||
|
||||
return response.Success(c, resp)
|
||||
}
|
||||
|
||||
// GetMe 获取当前用户信息
|
||||
func (h *AuthHandler) GetMe(c *fiber.Ctx) error {
|
||||
userID := middleware.GetUserIDFromContext(c.UserContext())
|
||||
if userID == 0 {
|
||||
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
|
||||
userInfo, permissions, err := h.authService.GetCurrentUser(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"user": userInfo,
|
||||
"permissions": permissions,
|
||||
}
|
||||
|
||||
return response.Success(c, data)
|
||||
}
|
||||
|
||||
// ChangePassword 修改密码
|
||||
func (h *AuthHandler) ChangePassword(c *fiber.Ctx) error {
|
||||
userID := middleware.GetUserIDFromContext(c.UserContext())
|
||||
if userID == 0 {
|
||||
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
var req dto.ChangePasswordRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
if err := h.validator.Struct(&req); err != nil {
|
||||
logger.GetAppLogger().Warn("参数验证失败",
|
||||
zap.String("path", c.Path()),
|
||||
zap.String("method", c.Method()),
|
||||
zap.Error(err),
|
||||
)
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
|
||||
if err := h.authService.ChangePassword(ctx, userID, req.OldPassword, req.NewPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, nil)
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package h5
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
enterpriseDeviceService "github.com/break/junhong_cmp_fiber/internal/service/enterprise_device"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
)
|
||||
|
||||
type EnterpriseDeviceHandler struct {
|
||||
service *enterpriseDeviceService.Service
|
||||
}
|
||||
|
||||
func NewEnterpriseDeviceHandler(service *enterpriseDeviceService.Service) *EnterpriseDeviceHandler {
|
||||
return &EnterpriseDeviceHandler{service: service}
|
||||
}
|
||||
|
||||
func (h *EnterpriseDeviceHandler) ListDevices(c *fiber.Ctx) error {
|
||||
var req dto.H5EnterpriseDeviceListReq
|
||||
if err := c.QueryParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
serviceReq := &dto.EnterpriseDeviceListReq{
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
VirtualNo: req.VirtualNo,
|
||||
}
|
||||
|
||||
result, err := h.service.ListDevicesForEnterprise(c.UserContext(), serviceReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.SuccessWithPagination(c, result.List, result.Total, req.Page, req.PageSize)
|
||||
}
|
||||
|
||||
func (h *EnterpriseDeviceHandler) GetDeviceDetail(c *fiber.Ctx) error {
|
||||
deviceIDStr := c.Params("device_id")
|
||||
deviceID, err := strconv.ParseUint(deviceIDStr, 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "设备ID格式错误")
|
||||
}
|
||||
|
||||
result, err := h.service.GetDeviceDetail(c.UserContext(), uint(deviceID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, result)
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
package h5
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
orderService "github.com/break/junhong_cmp_fiber/internal/service/order"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
)
|
||||
|
||||
type OrderHandler struct {
|
||||
service *orderService.Service
|
||||
}
|
||||
|
||||
func NewOrderHandler(service *orderService.Service) *OrderHandler {
|
||||
return &OrderHandler{service: service}
|
||||
}
|
||||
|
||||
func (h *OrderHandler) Create(c *fiber.Ctx) error {
|
||||
var req dto.CreateOrderRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
if req.PaymentMethod != model.PaymentMethodWallet {
|
||||
return errors.New(errors.CodeInvalidParam, "H5端只支持钱包支付")
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
|
||||
var buyerType string
|
||||
var buyerID uint
|
||||
|
||||
switch userType {
|
||||
case constants.UserTypeAgent:
|
||||
buyerType = model.BuyerTypeAgent
|
||||
buyerID = middleware.GetShopIDFromContext(ctx)
|
||||
case constants.UserTypeEnterprise:
|
||||
return errors.New(errors.CodeForbidden, "企业账号不支持在线购买")
|
||||
case constants.UserTypePersonalCustomer:
|
||||
buyerType = model.BuyerTypePersonal
|
||||
buyerID = middleware.GetCustomerIDFromContext(ctx)
|
||||
default:
|
||||
return errors.New(errors.CodeForbidden, "不支持的用户类型")
|
||||
}
|
||||
|
||||
order, err := h.service.CreateH5Order(ctx, &req, buyerType, buyerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, order)
|
||||
}
|
||||
|
||||
func (h *OrderHandler) Get(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "无效的订单ID")
|
||||
}
|
||||
|
||||
order, err := h.service.Get(c.UserContext(), uint(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, order)
|
||||
}
|
||||
|
||||
func (h *OrderHandler) List(c *fiber.Ctx) error {
|
||||
var req dto.OrderListRequest
|
||||
if err := c.QueryParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
|
||||
var buyerType string
|
||||
var buyerID uint
|
||||
|
||||
switch userType {
|
||||
case constants.UserTypeAgent:
|
||||
buyerType = model.BuyerTypeAgent
|
||||
buyerID = middleware.GetShopIDFromContext(ctx)
|
||||
case constants.UserTypePersonalCustomer:
|
||||
buyerType = model.BuyerTypePersonal
|
||||
buyerID = middleware.GetCustomerIDFromContext(ctx)
|
||||
default:
|
||||
return errors.New(errors.CodeForbidden, "不支持的用户类型")
|
||||
}
|
||||
|
||||
orders, err := h.service.List(ctx, &req, buyerType, buyerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, orders)
|
||||
}
|
||||
|
||||
func (h *OrderHandler) WalletPay(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "无效的订单ID")
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
|
||||
var buyerType string
|
||||
var buyerID uint
|
||||
|
||||
switch userType {
|
||||
case constants.UserTypeAgent:
|
||||
buyerType = model.BuyerTypeAgent
|
||||
buyerID = middleware.GetShopIDFromContext(ctx)
|
||||
case constants.UserTypePersonalCustomer:
|
||||
buyerType = model.BuyerTypePersonal
|
||||
buyerID = middleware.GetCustomerIDFromContext(ctx)
|
||||
default:
|
||||
return errors.New(errors.CodeForbidden, "不支持的用户类型")
|
||||
}
|
||||
|
||||
if err := h.service.WalletPay(ctx, uint(id), buyerType, buyerID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, nil)
|
||||
}
|
||||
|
||||
// WechatPayJSAPI 微信 JSAPI 支付
|
||||
// POST /api/h5/orders/:id/wechat-pay/jsapi
|
||||
func (h *OrderHandler) WechatPayJSAPI(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "无效的订单ID")
|
||||
}
|
||||
|
||||
var req dto.WechatPayJSAPIRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
|
||||
var buyerType string
|
||||
var buyerID uint
|
||||
|
||||
switch userType {
|
||||
case constants.UserTypeAgent:
|
||||
buyerType = model.BuyerTypeAgent
|
||||
buyerID = middleware.GetShopIDFromContext(ctx)
|
||||
case constants.UserTypePersonalCustomer:
|
||||
buyerType = model.BuyerTypePersonal
|
||||
buyerID = middleware.GetCustomerIDFromContext(ctx)
|
||||
default:
|
||||
return errors.New(errors.CodeForbidden, "不支持的用户类型")
|
||||
}
|
||||
|
||||
result, err := h.service.WechatPayJSAPI(ctx, uint(id), req.OpenID, buyerType, buyerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// WechatPayH5 微信 H5 支付
|
||||
// POST /api/h5/orders/:id/wechat-pay/h5
|
||||
func (h *OrderHandler) WechatPayH5(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "无效的订单ID")
|
||||
}
|
||||
|
||||
var req dto.WechatPayH5Request
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
|
||||
var buyerType string
|
||||
var buyerID uint
|
||||
|
||||
switch userType {
|
||||
case constants.UserTypeAgent:
|
||||
buyerType = model.BuyerTypeAgent
|
||||
buyerID = middleware.GetShopIDFromContext(ctx)
|
||||
case constants.UserTypePersonalCustomer:
|
||||
buyerType = model.BuyerTypePersonal
|
||||
buyerID = middleware.GetCustomerIDFromContext(ctx)
|
||||
default:
|
||||
return errors.New(errors.CodeForbidden, "不支持的用户类型")
|
||||
}
|
||||
|
||||
result, err := h.service.WechatPayH5(ctx, uint(id), &req.SceneInfo, buyerType, buyerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, result)
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package h5
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
packageService "github.com/break/junhong_cmp_fiber/internal/service/package"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// PackageUsageHandler H5 端套餐使用情况 Handler
|
||||
type PackageUsageHandler struct {
|
||||
db *gorm.DB
|
||||
customerViewService *packageService.CustomerViewService
|
||||
}
|
||||
|
||||
// NewPackageUsageHandler 创建 H5 端套餐使用情况 Handler
|
||||
func NewPackageUsageHandler(db *gorm.DB, customerViewService *packageService.CustomerViewService) *PackageUsageHandler {
|
||||
return &PackageUsageHandler{
|
||||
db: db,
|
||||
customerViewService: customerViewService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetMyUsage 任务 15.2-15.5: 获取我的套餐使用情况
|
||||
// GET /api/h5/packages/my-usage
|
||||
func (h *PackageUsageHandler) GetMyUsage(c *fiber.Ctx) error {
|
||||
ctx := c.UserContext()
|
||||
|
||||
// 任务 15.3: 从 JWT 上下文中提取用户信息
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
|
||||
var carrierType string
|
||||
var carrierID uint
|
||||
|
||||
// 根据用户类型获取载体信息
|
||||
switch userType {
|
||||
case constants.UserTypePersonalCustomer:
|
||||
// 个人客户:查询其订单关联的 IoT 卡或设备
|
||||
customerID := middleware.GetCustomerIDFromContext(ctx)
|
||||
if customerID == 0 {
|
||||
return errors.New(errors.CodeInvalidParam, "未找到客户信息")
|
||||
}
|
||||
|
||||
// 查询该客户的套餐使用记录,获取载体信息
|
||||
var usage model.PackageUsage
|
||||
err := h.db.WithContext(ctx).
|
||||
Joins("JOIN tb_order ON tb_order.id = tb_package_usage.order_id").
|
||||
Where("tb_order.buyer_type = ? AND tb_order.buyer_id = ?", model.BuyerTypePersonal, customerID).
|
||||
Where("tb_package_usage.status IN ?", []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusDepleted}).
|
||||
Order("tb_package_usage.activated_at DESC").
|
||||
First(&usage).Error
|
||||
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "未找到套餐使用记录")
|
||||
}
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "查询套餐使用记录失败")
|
||||
}
|
||||
|
||||
// 确定载体类型和 ID
|
||||
if usage.IotCardID > 0 {
|
||||
carrierType = "iot_card"
|
||||
carrierID = usage.IotCardID
|
||||
} else if usage.DeviceID > 0 {
|
||||
carrierType = "device"
|
||||
carrierID = usage.DeviceID
|
||||
} else {
|
||||
return errors.New(errors.CodeInvalidParam, "套餐使用记录未关联卡或设备")
|
||||
}
|
||||
|
||||
case constants.UserTypeAgent, constants.UserTypeEnterprise:
|
||||
// 代理和企业用户暂不支持通过此接口查询
|
||||
// 他们应该使用后台管理接口查询指定卡/设备的套餐情况
|
||||
return errors.New(errors.CodeForbidden, "此接口仅供个人客户使用")
|
||||
|
||||
default:
|
||||
return errors.New(errors.CodeForbidden, "不支持的用户类型")
|
||||
}
|
||||
|
||||
// 任务 15.4: 调用 CustomerViewService.GetMyUsage 获取流量数据
|
||||
usageData, err := h.customerViewService.GetMyUsage(ctx, carrierType, carrierID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 任务 15.5: 返回 PackageUsageCustomerViewResponse 响应
|
||||
return response.Success(c, usageData)
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
package h5
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
rechargeService "github.com/break/junhong_cmp_fiber/internal/service/recharge"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
)
|
||||
|
||||
// RechargeHandler 充值订单处理器
|
||||
// 提供充值订单的创建、预检、查询等接口
|
||||
type RechargeHandler struct {
|
||||
service *rechargeService.Service
|
||||
}
|
||||
|
||||
// NewRechargeHandler 创建充值订单处理器实例
|
||||
// 参数:
|
||||
// - service: 充值服务
|
||||
//
|
||||
// 返回:
|
||||
// - *RechargeHandler: 充值订单处理器实例
|
||||
func NewRechargeHandler(service *rechargeService.Service) *RechargeHandler {
|
||||
return &RechargeHandler{service: service}
|
||||
}
|
||||
|
||||
// Create 创建充值订单
|
||||
// POST /api/h5/wallets/recharge
|
||||
// 请求参数:
|
||||
// - resource_type: 资源类型(iot_card/device)
|
||||
// - resource_id: 资源ID(卡ID或设备ID)
|
||||
// - amount: 充值金额(分)
|
||||
// - payment_method: 支付方式(wechat/alipay)
|
||||
//
|
||||
// 响应:
|
||||
// - 成功: 返回充值订单信息(订单ID、订单号、金额、状态等)
|
||||
// - 失败: 返回错误信息
|
||||
func (h *RechargeHandler) Create(c *fiber.Ctx) error {
|
||||
var req dto.CreateRechargeRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
// 获取个人客户ID作为用户ID
|
||||
userID := middleware.GetCustomerIDFromContext(ctx)
|
||||
if userID == 0 {
|
||||
return errors.New(errors.CodeUnauthorized, "用户未登录")
|
||||
}
|
||||
|
||||
result, err := h.service.Create(ctx, &req, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// RechargeCheck 充值预检
|
||||
// GET /api/h5/wallets/recharge-check
|
||||
// 请求参数:
|
||||
// - resource_type: 资源类型(iot_card/device)
|
||||
// - resource_id: 资源ID(卡ID或设备ID)
|
||||
//
|
||||
// 响应:
|
||||
// - 成功: 返回预检信息(是否需要强充、强充金额、最小/最大充值金额等)
|
||||
// - 失败: 返回错误信息
|
||||
func (h *RechargeHandler) RechargeCheck(c *fiber.Ctx) error {
|
||||
var req dto.RechargeCheckRequest
|
||||
if err := c.QueryParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
// 验证必填参数
|
||||
if req.ResourceType == "" {
|
||||
return errors.New(errors.CodeInvalidParam, "资源类型不能为空")
|
||||
}
|
||||
if req.ResourceID == 0 {
|
||||
return errors.New(errors.CodeInvalidParam, "资源ID不能为空")
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
result, err := h.service.GetRechargeCheck(ctx, req.ResourceType, req.ResourceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 转换为 DTO 响应
|
||||
resp := &dto.RechargeCheckResponse{
|
||||
NeedForceRecharge: result.NeedForceRecharge,
|
||||
ForceRechargeAmount: result.ForceRechargeAmount,
|
||||
TriggerType: result.TriggerType,
|
||||
MinAmount: result.MinAmount,
|
||||
MaxAmount: result.MaxAmount,
|
||||
CurrentAccumulated: result.CurrentAccumulated,
|
||||
Threshold: result.Threshold,
|
||||
Message: result.Message,
|
||||
FirstCommissionPaid: result.FirstCommissionPaid,
|
||||
}
|
||||
|
||||
return response.Success(c, resp)
|
||||
}
|
||||
|
||||
// List 查询充值订单列表
|
||||
// GET /api/h5/wallets/recharges
|
||||
// 请求参数:
|
||||
// - page: 页码(从1开始,默认1)
|
||||
// - page_size: 每页数量(默认20,最大100)
|
||||
// - wallet_id: 钱包ID筛选(可选)
|
||||
// - status: 状态筛选(可选,1-待支付 2-已支付 3-已完成 4-已关闭 5-已退款)
|
||||
// - start_time: 开始时间筛选(可选)
|
||||
// - end_time: 结束时间筛选(可选)
|
||||
//
|
||||
// 响应:
|
||||
// - 成功: 返回充值订单列表(分页数据、总记录数、总页数)
|
||||
// - 失败: 返回错误信息
|
||||
func (h *RechargeHandler) List(c *fiber.Ctx) error {
|
||||
var req dto.RechargeListRequest
|
||||
if err := c.QueryParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
// 获取个人客户ID作为用户ID
|
||||
userID := middleware.GetCustomerIDFromContext(ctx)
|
||||
if userID == 0 {
|
||||
return errors.New(errors.CodeUnauthorized, "用户未登录")
|
||||
}
|
||||
|
||||
result, err := h.service.List(ctx, &req, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// Get 查询充值订单详情
|
||||
// GET /api/h5/wallets/recharges/:id
|
||||
// 路径参数:
|
||||
// - id: 充值订单ID
|
||||
//
|
||||
// 响应:
|
||||
// - 成功: 返回充值订单详情(订单ID、订单号、金额、状态、支付信息等)
|
||||
// - 失败: 返回错误信息
|
||||
func (h *RechargeHandler) Get(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "无效的充值订单ID")
|
||||
}
|
||||
|
||||
ctx := c.UserContext()
|
||||
// 获取个人客户ID作为用户ID
|
||||
userID := middleware.GetCustomerIDFromContext(ctx)
|
||||
if userID == 0 {
|
||||
return errors.New(errors.CodeUnauthorized, "用户未登录")
|
||||
}
|
||||
|
||||
result, err := h.service.GetByID(ctx, uint(id), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, result)
|
||||
}
|
||||
Reference in New Issue
Block a user