feat: 实名即生效套餐沉默期、延期办理与资产出库时间
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 8m39s
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 8m39s
- 迁移 000259~000265:套餐记录沉默期字段与扫描索引、套餐默认沉默期、延期政策/流水/订单/批次/明细 5 张表、默认政策与系统配置(总开关默认关闭)、企微审批白名单、卡与设备出库时间及出库记录 - 沉默期设置(订单发放、自动购包、钱包续费)、后台购买时调整(超默认值按政策免审或企微审批)、统一判定与激活闸门、排队与孤儿扫描跳过过期套餐、过期套餐禁止退款、每 10 分钟扫描 - 总开关专用接口与打开前 4 项检查;延期政策;延期资格与计费;H5 自助与后台二维码在线支付(商户池路由)、支付回调与查单恢复、线下登记、特殊/批量延期、异常重试与原路退款;沉默期审批场景与终态处理 - 换货沉默期流水与出库时间;卡/设备列表、详情、导出与出库记录接口;延期订单与即将过期套餐列表及导出;财务流水纳入延期支付 - 沉默期与出库时间回填工具 cmd/silence-backfill;前端接口变更说明 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
359
internal/handler/admin/package_silence.go
Normal file
359
internal/handler/admin/package_silence.go
Normal file
@@ -0,0 +1,359 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"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/model/dto"
|
||||
packagesilencequery "github.com/break/junhong_cmp_fiber/internal/query/packagesilence"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
)
|
||||
|
||||
// PackageSilenceHandler 后台沉默期:总开关、延期政策、延期资格、延期订单、线下登记、特殊延期、批次、异常、流水与出库记录。
|
||||
// Handler 只做绑定与参数校验,权限点、数据范围、金额与状态由用例判断。
|
||||
type PackageSilenceHandler struct {
|
||||
switches *packagesilenceapp.SwitchService
|
||||
policies *packagesilenceapp.PolicyService
|
||||
extensions *packagesilenceapp.ExtensionService
|
||||
query *packagesilencequery.Query
|
||||
validator *validator.Validate
|
||||
}
|
||||
|
||||
// NewPackageSilenceHandler 创建后台沉默期 Handler。
|
||||
func NewPackageSilenceHandler(
|
||||
switches *packagesilenceapp.SwitchService,
|
||||
policies *packagesilenceapp.PolicyService,
|
||||
extensions *packagesilenceapp.ExtensionService,
|
||||
query *packagesilencequery.Query,
|
||||
validator *validator.Validate,
|
||||
) *PackageSilenceHandler {
|
||||
return &PackageSilenceHandler{switches: switches, policies: policies, extensions: extensions, query: query, validator: validator}
|
||||
}
|
||||
|
||||
func (h *PackageSilenceHandler) bindQuery(c *fiber.Ctx, prefix string, req any) error {
|
||||
if err := c.QueryParser(req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
return h.validate(prefix, req)
|
||||
}
|
||||
|
||||
func (h *PackageSilenceHandler) bindBody(c *fiber.Ctx, prefix string, req any) error {
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
return h.validate(prefix, req)
|
||||
}
|
||||
|
||||
func (h *PackageSilenceHandler) validate(prefix string, req any) error {
|
||||
if h.validator == nil {
|
||||
return nil
|
||||
}
|
||||
if err := h.validator.Struct(req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, validationMessage(prefix, req, err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func silencePathID(c *fiber.Ctx) (uint, error) {
|
||||
id, err := c.ParamsInt("id")
|
||||
if err != nil || id <= 0 {
|
||||
return 0, errors.New(errors.CodeInvalidParam, "ID 无效")
|
||||
}
|
||||
return uint(id), nil
|
||||
}
|
||||
|
||||
// GetSwitch 查询沉默期总开关与打开前检查。GET /api/admin/package-silence/switch
|
||||
func (h *PackageSilenceHandler) GetSwitch(c *fiber.Ctx) error {
|
||||
result, err := h.switches.Status(c.UserContext())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// UpdateSwitch 切换沉默期总开关(仅超级管理员)。PUT /api/admin/package-silence/switch
|
||||
func (h *PackageSilenceHandler) UpdateSwitch(c *fiber.Ctx) error {
|
||||
var req dto.SilenceSwitchRequest
|
||||
if err := h.bindBody(c, "沉默期总开关参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.switches.Toggle(c.UserContext(), *req.Enabled)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// ListPolicies 延期政策列表。GET /api/admin/package-silence/policies
|
||||
func (h *PackageSilenceHandler) ListPolicies(c *fiber.Ctx) error {
|
||||
var req dto.SilencePolicyListRequest
|
||||
if err := h.bindQuery(c, "延期政策查询参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
items, total, err := h.policies.List(c.UserContext(), &req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
page, pageSize := req.Page, req.PageSize
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 {
|
||||
pageSize = 20
|
||||
}
|
||||
return response.SuccessWithPagination(c, items, total, page, pageSize)
|
||||
}
|
||||
|
||||
// GetPolicy 延期政策详情。GET /api/admin/package-silence/policies/:id
|
||||
func (h *PackageSilenceHandler) GetPolicy(c *fiber.Ctx) error {
|
||||
id, err := silencePathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.policies.Get(c.UserContext(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// CreatePolicy 新增延期政策。POST /api/admin/package-silence/policies
|
||||
func (h *PackageSilenceHandler) CreatePolicy(c *fiber.Ctx) error {
|
||||
var req dto.SilencePolicyRequest
|
||||
if err := h.bindBody(c, "延期政策参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.policies.Create(c.UserContext(), &req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// UpdatePolicy 编辑延期政策。PUT /api/admin/package-silence/policies/:id
|
||||
func (h *PackageSilenceHandler) UpdatePolicy(c *fiber.Ctx) error {
|
||||
id, err := silencePathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req dto.SilencePolicyRequest
|
||||
if err := h.bindBody(c, "延期政策参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.policies.Update(c.UserContext(), id, &req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// UpdatePolicyStatus 启停延期政策。PUT /api/admin/package-silence/policies/:id/status
|
||||
func (h *PackageSilenceHandler) UpdatePolicyStatus(c *fiber.Ctx) error {
|
||||
id, err := silencePathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req dto.SilencePolicyStatusRequest
|
||||
if err := h.bindBody(c, "延期政策状态参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.policies.SetStatus(c.UserContext(), id, *req.Status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// MatchPolicy 查询套餐当前匹配的延期政策。GET /api/admin/package-silence/policies/match
|
||||
func (h *PackageSilenceHandler) MatchPolicy(c *fiber.Ctx) error {
|
||||
var req dto.SilencePolicyMatchRequest
|
||||
if err := h.bindQuery(c, "政策匹配查询参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.policies.MatchForPackage(c.UserContext(), req.PackageID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// Eligibility 延期资格。GET /api/admin/package-silence/eligibility
|
||||
func (h *PackageSilenceHandler) Eligibility(c *fiber.Ctx) error {
|
||||
var req dto.SilenceEligibilityRequest
|
||||
if err := h.bindQuery(c, "延期资格查询参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.extensions.Eligibility(c.UserContext(), req.PackageUsageID, packagesilenceapp.Scope{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// Quote 按天数试算付费延期。GET /api/admin/package-silence/quote
|
||||
func (h *PackageSilenceHandler) Quote(c *fiber.Ctx) error {
|
||||
var req dto.SilenceQuoteRequest
|
||||
if err := h.bindQuery(c, "延期试算参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.extensions.Quote(c.UserContext(), req.PackageUsageID, req.Days, packagesilenceapp.Scope{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// CreateQRCodeOrder 运营创建在线延期订单并生成支付二维码。POST /api/admin/package-silence/orders/qrcode
|
||||
func (h *PackageSilenceHandler) CreateQRCodeOrder(c *fiber.Ctx) error {
|
||||
var req dto.SilenceOnlineOrderRequest
|
||||
if err := h.bindBody(c, "延期下单参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.extensions.CreateOnlineOrder(c.UserContext(), packagesilenceapp.OnlineOrderCommand{
|
||||
Request: req, Scope: packagesilenceapp.Scope{}, PayerClientIP: c.IP(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// RegisterOffline 登记线下付费延期。POST /api/admin/package-silence/orders/offline
|
||||
func (h *PackageSilenceHandler) RegisterOffline(c *fiber.Ctx) error {
|
||||
var req dto.SilenceOfflineOrderRequest
|
||||
if err := h.bindBody(c, "线下付费延期参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.extensions.RegisterOffline(c.UserContext(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// ListOrders 延期订单列表。GET /api/admin/package-silence/orders
|
||||
func (h *PackageSilenceHandler) ListOrders(c *fiber.Ctx) error {
|
||||
var req dto.SilenceOrderListRequest
|
||||
if err := h.bindQuery(c, "延期订单查询参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.query.ListOrders(c.UserContext(), packagesilencequery.OrderFilter{Request: req})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// GetOrder 延期订单详情。GET /api/admin/package-silence/orders/:id
|
||||
func (h *PackageSilenceHandler) GetOrder(c *fiber.Ctx) error {
|
||||
id, err := silencePathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.query.GetOrder(c.UserContext(), id, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// SubmitSpecial 提交特殊延期(单个 / 批量)。POST /api/admin/package-silence/special
|
||||
func (h *PackageSilenceHandler) SubmitSpecial(c *fiber.Ctx) error {
|
||||
var req dto.SilenceSpecialRequest
|
||||
if err := h.bindBody(c, "特殊延期参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.extensions.SubmitSpecial(c.UserContext(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// ListBatches 延期批次列表。GET /api/admin/package-silence/batches
|
||||
func (h *PackageSilenceHandler) ListBatches(c *fiber.Ctx) error {
|
||||
var req dto.SilenceBatchListRequest
|
||||
if err := h.bindQuery(c, "延期批次查询参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.query.ListBatches(c.UserContext(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// GetBatch 延期批次详情(含明细)。GET /api/admin/package-silence/batches/:id
|
||||
func (h *PackageSilenceHandler) GetBatch(c *fiber.Ctx) error {
|
||||
id, err := silencePathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.extensions.BatchDetail(c.UserContext(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// HandleException 异常明细重试 / 关闭。POST /api/admin/package-silence/extensions/:id/handle
|
||||
func (h *PackageSilenceHandler) HandleException(c *fiber.Ctx) error {
|
||||
id, err := silencePathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req dto.SilenceExceptionHandleRequest
|
||||
if err := h.bindBody(c, "异常处理参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.extensions.HandleException(c.UserContext(), id, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// ListRecords 套餐沉默期流水。GET /api/admin/package-silence/records
|
||||
func (h *PackageSilenceHandler) ListRecords(c *fiber.Ctx) error {
|
||||
var req dto.SilenceRecordListRequest
|
||||
if err := h.bindQuery(c, "沉默期流水查询参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.extensions.Records(c.UserContext(), req.PackageUsageID, packagesilenceapp.Scope{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// ListUsages 即将过期 / 已过期套餐。GET /api/admin/package-silence/usages
|
||||
func (h *PackageSilenceHandler) ListUsages(c *fiber.Ctx) error {
|
||||
var req dto.SilenceUsageListRequest
|
||||
if err := h.bindQuery(c, "沉默期套餐查询参数不合法", &req); err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := h.query.ListUsages(c.UserContext(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// ListOutboundRecords 资产各世代出库记录。GET /api/admin/assets/:asset_type/:id/outbound-records
|
||||
func (h *PackageSilenceHandler) ListOutboundRecords(c *fiber.Ctx) error {
|
||||
id, err := silencePathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
assetType := c.Params("asset_type")
|
||||
if assetType != "card" && assetType != "iot_card" && assetType != "device" {
|
||||
return errors.New(errors.CodeInvalidParam, "资产类型仅支持 card 或 device")
|
||||
}
|
||||
result, err := h.query.ListOutboundRecords(c.UserContext(), assetType, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
@@ -521,7 +521,9 @@ func (h *ClientAssetHandler) GetPackageHistory(c *fiber.Ctx) error {
|
||||
|
||||
items := make([]*dto.ClientAssetPackageHistoryNode, 0, len(history.Items))
|
||||
for _, node := range history.Items {
|
||||
items = append(items, buildClientPackageHistoryNode(node, packageMap))
|
||||
item := buildClientPackageHistoryNode(node, packageMap)
|
||||
h.fillClientSilenceViews(c.UserContext(), item, node)
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return response.SuccessWithPagination(c, items, history.Total, req.Page, req.PageSize)
|
||||
@@ -590,6 +592,19 @@ func buildClientPackageHistoryNode(node *assetquery.PackageHistoryNode, packageM
|
||||
return item
|
||||
}
|
||||
|
||||
// fillClientSilenceViews 按历史树逐节点填写沉默期展示信息(总开关关闭时不返回)。
|
||||
func (h *ClientAssetHandler) fillClientSilenceViews(ctx context.Context, item *dto.ClientAssetPackageHistoryNode, node *assetquery.PackageHistoryNode) {
|
||||
if item == nil || node == nil || h.assetService == nil {
|
||||
return
|
||||
}
|
||||
item.Silence = h.assetService.SilenceView(ctx, node.Usage)
|
||||
for index, child := range node.Children {
|
||||
if index < len(item.Children) {
|
||||
h.fillClientSilenceViews(ctx, item.Children[index], child)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RefreshAsset B4 资产刷新
|
||||
// POST /api/c/v1/asset/refresh
|
||||
func (h *ClientAssetHandler) RefreshAsset(c *fiber.Ctx) error {
|
||||
|
||||
166
internal/handler/app/client_package_silence.go
Normal file
166
internal/handler/app/client_package_silence.go
Normal file
@@ -0,0 +1,166 @@
|
||||
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)
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
agentrechargeApp "github.com/break/junhong_cmp_fiber/internal/application/agentrecharge"
|
||||
packagesilenceApp "github.com/break/junhong_cmp_fiber/internal/application/packagesilence"
|
||||
"github.com/break/junhong_cmp_fiber/internal/infrastructure/integrationlog"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
orderService "github.com/break/junhong_cmp_fiber/internal/service/order"
|
||||
@@ -52,6 +53,17 @@ type PaymentHandler struct {
|
||||
agentPaymentConfirm *agentrechargeApp.ConfirmOnlinePaymentService
|
||||
integration *integrationlog.Repository
|
||||
logger *zap.Logger
|
||||
silence SilencePaymentConfirmer
|
||||
}
|
||||
|
||||
// SilencePaymentConfirmer 确认沉默期延期订单支付(order_type = silence_extension)。
|
||||
type SilencePaymentConfirmer interface {
|
||||
ConfirmPayment(ctx context.Context, command packagesilenceApp.PaymentConfirmCommand) error
|
||||
}
|
||||
|
||||
// SetSilencePaymentConfirmer 注入沉默期延期支付确认用例。
|
||||
func (h *PaymentHandler) SetSilencePaymentConfirmer(confirmer SilencePaymentConfirmer) {
|
||||
h.silence = confirmer
|
||||
}
|
||||
|
||||
func NewPaymentHandler(
|
||||
@@ -197,6 +209,19 @@ func (h *PaymentHandler) dispatchPaymentRecordCallback(ctx context.Context, call
|
||||
}
|
||||
case model.PaymentOrderTypeAgentRecharge:
|
||||
return true, h.confirmAgentRechargePayment(ctx, callback, log)
|
||||
case constants.PaymentOrderTypeSilenceExtension:
|
||||
// 沉默期延期:回调按支付单冻结商户已验签,金额由用例按订单冻结金额核对;总开关关闭也照常处理。
|
||||
if h.silence == nil {
|
||||
processErr = errors.New(errors.CodeInternalError, "沉默期延期支付确认未配置")
|
||||
} else {
|
||||
processErr = h.silence.ConfirmPayment(auditcontext.With(ctx, auditcontext.Context{
|
||||
ActorKind: constants.AuditActorExternalSystem, ActorID: callback.Provider, ActorName: "支付渠道回调",
|
||||
Source: constants.AuditSourceCallback, CorrelationID: callback.PaymentNo,
|
||||
}), packagesilenceApp.PaymentConfirmCommand{
|
||||
PaymentNo: callback.PaymentNo, ThirdPartyTradeNo: callback.TransactionID,
|
||||
Amount: callback.Amount, PaidAt: callback.PaidAt,
|
||||
})
|
||||
}
|
||||
default:
|
||||
processErr = errors.New(errors.CodeInvalidStatus, "未知支付记录类型")
|
||||
}
|
||||
@@ -538,7 +563,7 @@ func (h *PaymentHandler) AlipayCallback(c *fiber.Ctx) error {
|
||||
MerchantIdentity: notification.AppId, Provider: constants.IntegrationProviderAlipay,
|
||||
RawPayload: c.Body(), ContentType: c.Get("Content-Type"),
|
||||
}
|
||||
if payment.OrderType == model.PaymentOrderTypeAgentRecharge {
|
||||
if payment.OrderType == model.PaymentOrderTypeAgentRecharge || payment.OrderType == constants.PaymentOrderTypeSilenceExtension {
|
||||
paidAt, parseErr := time.ParseInLocation("2006-01-02 15:04:05", notification.GmtPayment, time.Local)
|
||||
if parseErr != nil {
|
||||
h.logger.Error("支付宝回调:付款时间格式无效",
|
||||
|
||||
Reference in New Issue
Block a user