All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m16s
核心改造: - 增量算法:流量计算从覆盖式改为增量累加(gateway - lastReading),支持上游运营商重置检测 - 日流量缓冲:insertDataUsageRecord 改为 Redis INCRBYFLOAT,每日凌晨落盘到 tb_card_daily_usage - 运营商:新增 data_reset_day 字段(联通=27,其余=1) - IoT卡:新增 last_gateway_reading_mb 字段存储上次网关读数 - 查询层:新建 TrafficQueryService 合并 Redis(今日)+ DB(历史)数据源 - 清理:删除 DataUsageRecord model/store,移除 polling_handler 旧引用 迁移:000094-000097(carrier字段、iot_card字段、数据初始化、日流量表)
207 lines
6.0 KiB
Go
207 lines
6.0 KiB
Go
package carrier
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/break/junhong_cmp_fiber/internal/model"
|
|
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
|
"github.com/break/junhong_cmp_fiber/internal/store"
|
|
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
|
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
|
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
|
)
|
|
|
|
type Service struct {
|
|
carrierStore *postgres.CarrierStore
|
|
}
|
|
|
|
func New(carrierStore *postgres.CarrierStore) *Service {
|
|
return &Service{carrierStore: carrierStore}
|
|
}
|
|
|
|
func (s *Service) Create(ctx context.Context, req *dto.CreateCarrierRequest) (*dto.CarrierResponse, error) {
|
|
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
if currentUserID == 0 {
|
|
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
}
|
|
|
|
existing, _ := s.carrierStore.GetByCode(ctx, req.CarrierCode)
|
|
if existing != nil {
|
|
return nil, errors.New(errors.CodeCarrierCodeExists, "运营商编码已存在")
|
|
}
|
|
|
|
carrier := &model.Carrier{
|
|
CarrierCode: req.CarrierCode,
|
|
CarrierName: req.CarrierName,
|
|
CarrierType: req.CarrierType,
|
|
Description: req.Description,
|
|
Status: constants.StatusEnabled,
|
|
DataResetDay: 1,
|
|
}
|
|
if req.DataResetDay != nil {
|
|
carrier.DataResetDay = *req.DataResetDay
|
|
}
|
|
if req.RealnameLinkType != nil {
|
|
carrier.RealnameLinkType = *req.RealnameLinkType
|
|
}
|
|
if req.RealnameLinkTemplate != nil {
|
|
carrier.RealnameLinkTemplate = *req.RealnameLinkTemplate
|
|
}
|
|
carrier.Creator = currentUserID
|
|
|
|
if err := s.carrierStore.Create(ctx, carrier); err != nil {
|
|
return nil, errors.Wrap(errors.CodeInternalError, err, "创建运营商失败")
|
|
}
|
|
|
|
return s.toResponse(carrier), nil
|
|
}
|
|
|
|
func (s *Service) Get(ctx context.Context, id uint) (*dto.CarrierResponse, error) {
|
|
carrier, err := s.carrierStore.GetByID(ctx, id)
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return nil, errors.New(errors.CodeCarrierNotFound, "运营商不存在")
|
|
}
|
|
return nil, errors.Wrap(errors.CodeInternalError, err, "获取运营商失败")
|
|
}
|
|
return s.toResponse(carrier), nil
|
|
}
|
|
|
|
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateCarrierRequest) (*dto.CarrierResponse, error) {
|
|
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
if currentUserID == 0 {
|
|
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
}
|
|
|
|
carrier, err := s.carrierStore.GetByID(ctx, id)
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return nil, errors.New(errors.CodeCarrierNotFound, "运营商不存在")
|
|
}
|
|
return nil, errors.Wrap(errors.CodeInternalError, err, "获取运营商失败")
|
|
}
|
|
|
|
if req.CarrierName != nil {
|
|
carrier.CarrierName = *req.CarrierName
|
|
}
|
|
if req.Description != nil {
|
|
carrier.Description = *req.Description
|
|
}
|
|
if req.DataResetDay != nil {
|
|
carrier.DataResetDay = *req.DataResetDay
|
|
}
|
|
if req.RealnameLinkType != nil {
|
|
carrier.RealnameLinkType = *req.RealnameLinkType
|
|
}
|
|
if req.RealnameLinkTemplate != nil {
|
|
carrier.RealnameLinkTemplate = *req.RealnameLinkTemplate
|
|
}
|
|
if carrier.RealnameLinkType == "template" && carrier.RealnameLinkTemplate == "" {
|
|
return nil, errors.New(errors.CodeInvalidParam, "模板URL类型必须提供实名链接模板")
|
|
}
|
|
carrier.Updater = currentUserID
|
|
|
|
if err := s.carrierStore.Update(ctx, carrier); err != nil {
|
|
return nil, errors.Wrap(errors.CodeInternalError, err, "更新运营商失败")
|
|
}
|
|
|
|
return s.toResponse(carrier), nil
|
|
}
|
|
|
|
func (s *Service) Delete(ctx context.Context, id uint) error {
|
|
_, err := s.carrierStore.GetByID(ctx, id)
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return errors.New(errors.CodeCarrierNotFound, "运营商不存在")
|
|
}
|
|
return errors.Wrap(errors.CodeInternalError, err, "获取运营商失败")
|
|
}
|
|
|
|
if err := s.carrierStore.Delete(ctx, id); err != nil {
|
|
return errors.Wrap(errors.CodeInternalError, err, "删除运营商失败")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) List(ctx context.Context, req *dto.CarrierListRequest) ([]*dto.CarrierResponse, int64, error) {
|
|
opts := &store.QueryOptions{
|
|
Page: req.Page,
|
|
PageSize: req.PageSize,
|
|
OrderBy: "id DESC",
|
|
}
|
|
if opts.Page == 0 {
|
|
opts.Page = 1
|
|
}
|
|
if opts.PageSize == 0 {
|
|
opts.PageSize = constants.DefaultPageSize
|
|
}
|
|
|
|
filters := make(map[string]interface{})
|
|
if req.CarrierType != nil {
|
|
filters["carrier_type"] = *req.CarrierType
|
|
}
|
|
if req.CarrierName != nil {
|
|
filters["carrier_name"] = *req.CarrierName
|
|
}
|
|
if req.Status != nil {
|
|
filters["status"] = *req.Status
|
|
}
|
|
|
|
carriers, total, err := s.carrierStore.List(ctx, opts, filters)
|
|
if err != nil {
|
|
return nil, 0, errors.Wrap(errors.CodeInternalError, err, "查询运营商列表失败")
|
|
}
|
|
|
|
responses := make([]*dto.CarrierResponse, len(carriers))
|
|
for i, c := range carriers {
|
|
responses[i] = s.toResponse(c)
|
|
}
|
|
|
|
return responses, total, nil
|
|
}
|
|
|
|
func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
|
|
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
if currentUserID == 0 {
|
|
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
}
|
|
|
|
carrier, err := s.carrierStore.GetByID(ctx, id)
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return errors.New(errors.CodeCarrierNotFound, "运营商不存在")
|
|
}
|
|
return errors.Wrap(errors.CodeInternalError, err, "获取运营商失败")
|
|
}
|
|
|
|
carrier.Status = status
|
|
carrier.Updater = currentUserID
|
|
|
|
if err := s.carrierStore.Update(ctx, carrier); err != nil {
|
|
return errors.Wrap(errors.CodeInternalError, err, "更新运营商状态失败")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) toResponse(c *model.Carrier) *dto.CarrierResponse {
|
|
return &dto.CarrierResponse{
|
|
ID: c.ID,
|
|
CarrierCode: c.CarrierCode,
|
|
CarrierName: c.CarrierName,
|
|
CarrierType: c.CarrierType,
|
|
Description: c.Description,
|
|
DataResetDay: c.DataResetDay,
|
|
RealnameLinkType: c.RealnameLinkType,
|
|
RealnameLinkTemplate: c.RealnameLinkTemplate,
|
|
Status: c.Status,
|
|
CreatedAt: c.CreatedAt.Format(time.RFC3339),
|
|
UpdatedAt: c.UpdatedAt.Format(time.RFC3339),
|
|
}
|
|
}
|