Files
junhong_cmp_fiber/internal/handler/app/client_package_silence.go
邹进 909f4d0ec2
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 8m39s
feat: 实名即生效套餐沉默期、延期办理与资产出库时间
- 迁移 000259~000265:套餐记录沉默期字段与扫描索引、套餐默认沉默期、延期政策/流水/订单/批次/明细 5 张表、默认政策与系统配置(总开关默认关闭)、企微审批白名单、卡与设备出库时间及出库记录
- 沉默期设置(订单发放、自动购包、钱包续费)、后台购买时调整(超默认值按政策免审或企微审批)、统一判定与激活闸门、排队与孤儿扫描跳过过期套餐、过期套餐禁止退款、每 10 分钟扫描
- 总开关专用接口与打开前 4 项检查;延期政策;延期资格与计费;H5 自助与后台二维码在线支付(商户池路由)、支付回调与查单恢复、线下登记、特殊/批量延期、异常重试与原路退款;沉默期审批场景与终态处理
- 换货沉默期流水与出库时间;卡/设备列表、详情、导出与出库记录接口;延期订单与即将过期套餐列表及导出;财务流水纳入延期支付
- 沉默期与出库时间回填工具 cmd/silence-backfill;前端接口变更说明

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 16:32:58 +09:00

167 lines
5.4 KiB
Go

package app
import (
"context"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
packagesilenceapp "github.com/break/junhong_cmp_fiber/internal/application/packagesilence"
"github.com/break/junhong_cmp_fiber/internal/middleware"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
packagesilencequery "github.com/break/junhong_cmp_fiber/internal/query/packagesilence"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/response"
)
// ClientPackageSilenceHandler H5 沉默期延期:资格、试算、自助下单、我的延期订单与沉默期流水。
// 资产归属由用例按当前客户校验;金额与月数全部由后端计算。
type ClientPackageSilenceHandler struct {
extensions *packagesilenceapp.ExtensionService
query *packagesilencequery.Query
validator *validator.Validate
}
// NewClientPackageSilenceHandler 创建 H5 沉默期 Handler。
func NewClientPackageSilenceHandler(extensions *packagesilenceapp.ExtensionService, query *packagesilencequery.Query, validator *validator.Validate) *ClientPackageSilenceHandler {
return &ClientPackageSilenceHandler{extensions: extensions, query: query, validator: validator}
}
// customerContext 返回当前客户 ID 与跳过后台数据范围的上下文(资产归属另行校验)。
func (h *ClientPackageSilenceHandler) customerContext(c *fiber.Ctx) (context.Context, uint, error) {
customerID, ok := middleware.GetCustomerID(c)
if !ok || customerID == 0 {
return nil, 0, errors.New(errors.CodeUnauthorized)
}
ctx := context.WithValue(c.UserContext(), constants.ContextKeySubordinateShopIDs, []uint{})
return ctx, customerID, nil
}
func (h *ClientPackageSilenceHandler) validate(req any) error {
if h.validator == nil {
return nil
}
if err := h.validator.Struct(req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数不合法")
}
return nil
}
// Eligibility 延期资格。GET /api/c/v1/package-silence/eligibility
func (h *ClientPackageSilenceHandler) Eligibility(c *fiber.Ctx) error {
var req dto.SilenceEligibilityRequest
if err := c.QueryParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam)
}
if err := h.validate(&req); err != nil {
return err
}
ctx, customerID, err := h.customerContext(c)
if err != nil {
return err
}
result, err := h.extensions.Eligibility(ctx, req.PackageUsageID, packagesilenceapp.Scope{CustomerID: customerID})
if err != nil {
return err
}
return response.Success(c, result)
}
// Quote 按天数试算。GET /api/c/v1/package-silence/quote
func (h *ClientPackageSilenceHandler) Quote(c *fiber.Ctx) error {
var req dto.SilenceQuoteRequest
if err := c.QueryParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam)
}
if err := h.validate(&req); err != nil {
return err
}
ctx, customerID, err := h.customerContext(c)
if err != nil {
return err
}
result, err := h.extensions.Quote(ctx, req.PackageUsageID, req.Days, packagesilenceapp.Scope{CustomerID: customerID})
if err != nil {
return err
}
return response.Success(c, result)
}
// CreateOrder 客户自助下单付费延期。POST /api/c/v1/package-silence/orders
func (h *ClientPackageSilenceHandler) CreateOrder(c *fiber.Ctx) error {
var req dto.SilenceOnlineOrderRequest
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam)
}
if err := h.validate(&req); err != nil {
return err
}
ctx, customerID, err := h.customerContext(c)
if err != nil {
return err
}
req.EvidenceRefs = nil
result, err := h.extensions.CreateOnlineOrder(ctx, packagesilenceapp.OnlineOrderCommand{
Request: req, Scope: packagesilenceapp.Scope{CustomerID: customerID}, PayerClientIP: c.IP(),
})
if err != nil {
return err
}
return response.Success(c, result)
}
// ListOrders 我的延期订单。GET /api/c/v1/package-silence/orders
func (h *ClientPackageSilenceHandler) ListOrders(c *fiber.Ctx) error {
var req dto.SilenceOrderListRequest
if err := c.QueryParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam)
}
ctx, customerID, err := h.customerContext(c)
if err != nil {
return err
}
result, err := h.query.ListOrders(ctx, packagesilencequery.OrderFilter{Request: req, CustomerID: customerID})
if err != nil {
return err
}
return response.Success(c, result)
}
// GetOrder 我的延期订单详情。GET /api/c/v1/package-silence/orders/:id
func (h *ClientPackageSilenceHandler) GetOrder(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
return errors.New(errors.CodeInvalidParam)
}
ctx, customerID, err := h.customerContext(c)
if err != nil {
return err
}
result, err := h.query.GetOrder(ctx, uint(id), customerID)
if err != nil {
return err
}
return response.Success(c, result)
}
// ListRecords 套餐沉默期流水。GET /api/c/v1/package-silence/records
func (h *ClientPackageSilenceHandler) ListRecords(c *fiber.Ctx) error {
var req dto.SilenceRecordListRequest
if err := c.QueryParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam)
}
if err := h.validate(&req); err != nil {
return err
}
ctx, customerID, err := h.customerContext(c)
if err != nil {
return err
}
result, err := h.extensions.Records(ctx, req.PackageUsageID, packagesilenceapp.Scope{CustomerID: customerID})
if err != nil {
return err
}
return response.Success(c, result)
}