feat: 卡流量读数先过采集队列再入账,扣减按采集时刻确定套餐
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m27s

- 每卡 Redis 采集队列(默认 N=3、跨天清空、回退容差 1 MB),攒满且不下降才写最后一个点
- 测试环境全量切换日快照计量,下线灰度名单、切换水位与卡表基线扣费;全部卡置待建起点
- 读数区分没给值与 0;有套餐的卡读数 0 也写快照与日用量 0 行;重置日比较基准为 0 照常防旧值
- 新增采集记录(按月分区保留 90 天)、人工冲正流水与后台接口、采集告警
- 扣减按采集时刻有效套餐查找,只对生效中套餐做状态迁移,查不到套餐按成功跳过
- 换货新卡(新设备下的卡)打待建起点;采集间隔上限只校验启用中的轮询配置

Change: add-card-traffic-queue-and-observed-deduction

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
邹进
2026-10-29 12:18:47 +09:00
parent c33cc6f6c3
commit 9c3c258f16
55 changed files with 3142 additions and 881 deletions

View File

@@ -0,0 +1,87 @@
package admin
import (
"github.com/gofiber/fiber/v2"
"github.com/break/junhong_cmp_fiber/internal/handler/validation"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/response"
)
// AdjustPackageTraffic 人工冲正套餐流量
// POST /api/admin/assets/:identifier/packages/:package_usage_id/traffic-adjustments
func (h *AssetHandler) AdjustPackageTraffic(c *fiber.Ctx) error {
if err := rejectEnterpriseAssetAdmin(c); err != nil {
return err
}
asset, packageUsageID, err := h.resolveAssetPackageUsagePath(c)
if err != nil {
return err
}
var req dto.AdjustAssetPackageTrafficRequest
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数格式不正确")
}
req.PackageUsageID = packageUsageID
if err := h.validator.Struct(&req); err != nil {
return errors.New(errors.CodeInvalidParam, validation.Message("冲正参数不合法", &req, err))
}
result, err := h.assetService.AdjustPackageTraffic(c.UserContext(), asset.AssetType, asset.AssetID, assetIdentifierForAudit(asset), &req)
if err != nil {
return err
}
return response.Success(c, result)
}
// ListPackageTrafficAdjustments 查询套餐冲正流水
// GET /api/admin/assets/:identifier/packages/:package_usage_id/traffic-adjustments
func (h *AssetHandler) ListPackageTrafficAdjustments(c *fiber.Ctx) error {
if err := rejectEnterpriseAssetAdmin(c); err != nil {
return err
}
asset, packageUsageID, err := h.resolveAssetPackageUsagePath(c)
if err != nil {
return err
}
var req dto.PackageTrafficAdjustmentListRequest
if err := c.QueryParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数格式不正确")
}
req.PackageUsageID = packageUsageID
if err := h.validator.Struct(&req); err != nil {
return errors.New(errors.CodeInvalidParam, validation.Message("冲正流水查询参数不合法", &req, err))
}
result, err := h.assetService.ListPackageTrafficAdjustments(c.UserContext(), asset.AssetType, asset.AssetID, &req)
if err != nil {
return err
}
return response.Success(c, result)
}
// ListTrafficCollections 查询卡流量采集记录
// GET /api/admin/assets/:identifier/traffic-collections
func (h *AssetHandler) ListTrafficCollections(c *fiber.Ctx) error {
if err := rejectEnterpriseAssetAdmin(c); err != nil {
return err
}
asset, err := h.resolveByIdentifier(c)
if err != nil {
return err
}
if asset.AssetType != "card" {
return errors.New(errors.CodeInvalidParam, "采集记录只支持按卡查询")
}
var req dto.CardTrafficCollectionListRequest
if err := c.QueryParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数格式不正确")
}
if err := h.validator.Struct(&req); err != nil {
return errors.New(errors.CodeInvalidParam, validation.Message("采集记录查询参数不合法", &req, err))
}
result, err := h.assetService.ListCardTrafficCollections(c.UserContext(), asset.AssetID, &req)
if err != nil {
return err
}
return response.Success(c, result)
}