fix(channel-monitor): drop soft delete, refactor feature flag to declarative form

### 后端修复:日志表不该用软删除

channel_monitor_histories / channel_monitor_daily_rollups 都是日志/聚合表,
没有恢复需求。110 里加的 SoftDeleteMixin 会让 DELETE 自动变成 UPDATE deleted_at,
导致行和索引只增不减,徒增磁盘占用和查询成本。

改回分批物理删(参考 OpsCleanupService.deleteOldRowsByID 模板):

- ent schema 移除 SoftDeleteMixin,重新 go generate
- repo 新增 deleteChannelMonitorBatched 辅助 + 两条 prune SQL 常量
  (WITH batch AS SELECT id LIMIT 5000 → DELETE IN batch)
- DeleteHistoryBefore / DeleteRollupsBefore 改调分批 raw SQL
- 移除 ComputeAvailability / ComputeAvailabilityForMonitors / UpsertDailyRollupsFor /
  ListLatestPerModel / ListLatestForMonitorIDs / ListRecentHistoryForMonitors 等
  raw SQL 中的 deleted_at IS NULL 过滤
- UpsertDailyRollupsFor 的 ON CONFLICT 去掉 deleted_at = NULL 重置
- migration 111 DROP COLUMN deleted_at + 对应索引(110 已部署但 maintenance
  首跑在次日 02:00,此时尚无业务数据在依赖软删除)

### 前端重构:feature flag 声明式 + 复用

AppSidebar.vue 里 7 处 `...(flag ? [item] : [])` 样板代码删光,改为 NavItem 加
featureFlag?: () => boolean | undefined 字段,加一个 applyFeatureFlags 递归
过滤(含 children)。语义统一为 `!== false`(宽容策略,undefined 时默认显示,
避免 public settings 未加载完成时菜单闪烁消失 — 对应用户反馈"刷新后菜单消失
要去保存设置才回来")。

- 集中声明 4 个 flag getter:flagChannelMonitor / flagPayment /
  flagOpsMonitoring / flagAdminPayment
- 提取 buildSelfNavItems 复用用户端主菜单和管理员"我的账户"子菜单
- 未来新增开关:在统一位置加一个 flag getter + 给对应 NavItem 加字段
  (不用再动渲染逻辑)

bump 0.1.114.29
This commit is contained in:
erio
2026-04-21 10:45:30 +08:00
parent 8cf83c984e
commit ef6ec8a15a
21 changed files with 188 additions and 802 deletions

View File

@@ -10022,7 +10022,6 @@ type ChannelMonitorDailyRollupMutation struct {
op Op
typ string
id *int64
deleted_at *time.Time
model *string
bucket_date *time.Time
total_checks *int
@@ -10152,55 +10151,6 @@ func (m *ChannelMonitorDailyRollupMutation) IDs(ctx context.Context) ([]int64, e
}
}
// SetDeletedAt sets the "deleted_at" field.
func (m *ChannelMonitorDailyRollupMutation) SetDeletedAt(t time.Time) {
m.deleted_at = &t
}
// DeletedAt returns the value of the "deleted_at" field in the mutation.
func (m *ChannelMonitorDailyRollupMutation) DeletedAt() (r time.Time, exists bool) {
v := m.deleted_at
if v == nil {
return
}
return *v, true
}
// OldDeletedAt returns the old "deleted_at" field's value of the ChannelMonitorDailyRollup entity.
// If the ChannelMonitorDailyRollup object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *ChannelMonitorDailyRollupMutation) OldDeletedAt(ctx context.Context) (v *time.Time, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldDeletedAt is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldDeletedAt requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldDeletedAt: %w", err)
}
return oldValue.DeletedAt, nil
}
// ClearDeletedAt clears the value of the "deleted_at" field.
func (m *ChannelMonitorDailyRollupMutation) ClearDeletedAt() {
m.deleted_at = nil
m.clearedFields[channelmonitordailyrollup.FieldDeletedAt] = struct{}{}
}
// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation.
func (m *ChannelMonitorDailyRollupMutation) DeletedAtCleared() bool {
_, ok := m.clearedFields[channelmonitordailyrollup.FieldDeletedAt]
return ok
}
// ResetDeletedAt resets all changes to the "deleted_at" field.
func (m *ChannelMonitorDailyRollupMutation) ResetDeletedAt() {
m.deleted_at = nil
delete(m.clearedFields, channelmonitordailyrollup.FieldDeletedAt)
}
// SetMonitorID sets the "monitor_id" field.
func (m *ChannelMonitorDailyRollupMutation) SetMonitorID(i int64) {
m.monitor = &i
@@ -10966,10 +10916,7 @@ func (m *ChannelMonitorDailyRollupMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *ChannelMonitorDailyRollupMutation) Fields() []string {
fields := make([]string, 0, 15)
if m.deleted_at != nil {
fields = append(fields, channelmonitordailyrollup.FieldDeletedAt)
}
fields := make([]string, 0, 14)
if m.monitor != nil {
fields = append(fields, channelmonitordailyrollup.FieldMonitorID)
}
@@ -11020,8 +10967,6 @@ func (m *ChannelMonitorDailyRollupMutation) Fields() []string {
// schema.
func (m *ChannelMonitorDailyRollupMutation) Field(name string) (ent.Value, bool) {
switch name {
case channelmonitordailyrollup.FieldDeletedAt:
return m.DeletedAt()
case channelmonitordailyrollup.FieldMonitorID:
return m.MonitorID()
case channelmonitordailyrollup.FieldModel:
@@ -11059,8 +11004,6 @@ func (m *ChannelMonitorDailyRollupMutation) Field(name string) (ent.Value, bool)
// database failed.
func (m *ChannelMonitorDailyRollupMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
switch name {
case channelmonitordailyrollup.FieldDeletedAt:
return m.OldDeletedAt(ctx)
case channelmonitordailyrollup.FieldMonitorID:
return m.OldMonitorID(ctx)
case channelmonitordailyrollup.FieldModel:
@@ -11098,13 +11041,6 @@ func (m *ChannelMonitorDailyRollupMutation) OldField(ctx context.Context, name s
// type.
func (m *ChannelMonitorDailyRollupMutation) SetField(name string, value ent.Value) error {
switch name {
case channelmonitordailyrollup.FieldDeletedAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetDeletedAt(v)
return nil
case channelmonitordailyrollup.FieldMonitorID:
v, ok := value.(int64)
if !ok {
@@ -11355,11 +11291,7 @@ func (m *ChannelMonitorDailyRollupMutation) AddField(name string, value ent.Valu
// ClearedFields returns all nullable fields that were cleared during this
// mutation.
func (m *ChannelMonitorDailyRollupMutation) ClearedFields() []string {
var fields []string
if m.FieldCleared(channelmonitordailyrollup.FieldDeletedAt) {
fields = append(fields, channelmonitordailyrollup.FieldDeletedAt)
}
return fields
return nil
}
// FieldCleared returns a boolean indicating if a field with the given name was
@@ -11372,11 +11304,6 @@ func (m *ChannelMonitorDailyRollupMutation) FieldCleared(name string) bool {
// ClearField clears the value of the field with the given name. It returns an
// error if the field is not defined in the schema.
func (m *ChannelMonitorDailyRollupMutation) ClearField(name string) error {
switch name {
case channelmonitordailyrollup.FieldDeletedAt:
m.ClearDeletedAt()
return nil
}
return fmt.Errorf("unknown ChannelMonitorDailyRollup nullable field %s", name)
}
@@ -11384,9 +11311,6 @@ func (m *ChannelMonitorDailyRollupMutation) ClearField(name string) error {
// It returns an error if the field is not defined in the schema.
func (m *ChannelMonitorDailyRollupMutation) ResetField(name string) error {
switch name {
case channelmonitordailyrollup.FieldDeletedAt:
m.ResetDeletedAt()
return nil
case channelmonitordailyrollup.FieldMonitorID:
m.ResetMonitorID()
return nil
@@ -11513,7 +11437,6 @@ type ChannelMonitorHistoryMutation struct {
op Op
typ string
id *int64
deleted_at *time.Time
model *string
status *channelmonitorhistory.Status
latency_ms *int
@@ -11628,55 +11551,6 @@ func (m *ChannelMonitorHistoryMutation) IDs(ctx context.Context) ([]int64, error
}
}
// SetDeletedAt sets the "deleted_at" field.
func (m *ChannelMonitorHistoryMutation) SetDeletedAt(t time.Time) {
m.deleted_at = &t
}
// DeletedAt returns the value of the "deleted_at" field in the mutation.
func (m *ChannelMonitorHistoryMutation) DeletedAt() (r time.Time, exists bool) {
v := m.deleted_at
if v == nil {
return
}
return *v, true
}
// OldDeletedAt returns the old "deleted_at" field's value of the ChannelMonitorHistory entity.
// If the ChannelMonitorHistory object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *ChannelMonitorHistoryMutation) OldDeletedAt(ctx context.Context) (v *time.Time, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldDeletedAt is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldDeletedAt requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldDeletedAt: %w", err)
}
return oldValue.DeletedAt, nil
}
// ClearDeletedAt clears the value of the "deleted_at" field.
func (m *ChannelMonitorHistoryMutation) ClearDeletedAt() {
m.deleted_at = nil
m.clearedFields[channelmonitorhistory.FieldDeletedAt] = struct{}{}
}
// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation.
func (m *ChannelMonitorHistoryMutation) DeletedAtCleared() bool {
_, ok := m.clearedFields[channelmonitorhistory.FieldDeletedAt]
return ok
}
// ResetDeletedAt resets all changes to the "deleted_at" field.
func (m *ChannelMonitorHistoryMutation) ResetDeletedAt() {
m.deleted_at = nil
delete(m.clearedFields, channelmonitorhistory.FieldDeletedAt)
}
// SetMonitorID sets the "monitor_id" field.
func (m *ChannelMonitorHistoryMutation) SetMonitorID(i int64) {
m.monitor = &i
@@ -12071,10 +11945,7 @@ func (m *ChannelMonitorHistoryMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *ChannelMonitorHistoryMutation) Fields() []string {
fields := make([]string, 0, 8)
if m.deleted_at != nil {
fields = append(fields, channelmonitorhistory.FieldDeletedAt)
}
fields := make([]string, 0, 7)
if m.monitor != nil {
fields = append(fields, channelmonitorhistory.FieldMonitorID)
}
@@ -12104,8 +11975,6 @@ func (m *ChannelMonitorHistoryMutation) Fields() []string {
// schema.
func (m *ChannelMonitorHistoryMutation) Field(name string) (ent.Value, bool) {
switch name {
case channelmonitorhistory.FieldDeletedAt:
return m.DeletedAt()
case channelmonitorhistory.FieldMonitorID:
return m.MonitorID()
case channelmonitorhistory.FieldModel:
@@ -12129,8 +11998,6 @@ func (m *ChannelMonitorHistoryMutation) Field(name string) (ent.Value, bool) {
// database failed.
func (m *ChannelMonitorHistoryMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
switch name {
case channelmonitorhistory.FieldDeletedAt:
return m.OldDeletedAt(ctx)
case channelmonitorhistory.FieldMonitorID:
return m.OldMonitorID(ctx)
case channelmonitorhistory.FieldModel:
@@ -12154,13 +12021,6 @@ func (m *ChannelMonitorHistoryMutation) OldField(ctx context.Context, name strin
// type.
func (m *ChannelMonitorHistoryMutation) SetField(name string, value ent.Value) error {
switch name {
case channelmonitorhistory.FieldDeletedAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetDeletedAt(v)
return nil
case channelmonitorhistory.FieldMonitorID:
v, ok := value.(int64)
if !ok {
@@ -12267,9 +12127,6 @@ func (m *ChannelMonitorHistoryMutation) AddField(name string, value ent.Value) e
// mutation.
func (m *ChannelMonitorHistoryMutation) ClearedFields() []string {
var fields []string
if m.FieldCleared(channelmonitorhistory.FieldDeletedAt) {
fields = append(fields, channelmonitorhistory.FieldDeletedAt)
}
if m.FieldCleared(channelmonitorhistory.FieldLatencyMs) {
fields = append(fields, channelmonitorhistory.FieldLatencyMs)
}
@@ -12293,9 +12150,6 @@ func (m *ChannelMonitorHistoryMutation) FieldCleared(name string) bool {
// error if the field is not defined in the schema.
func (m *ChannelMonitorHistoryMutation) ClearField(name string) error {
switch name {
case channelmonitorhistory.FieldDeletedAt:
m.ClearDeletedAt()
return nil
case channelmonitorhistory.FieldLatencyMs:
m.ClearLatencyMs()
return nil
@@ -12313,9 +12167,6 @@ func (m *ChannelMonitorHistoryMutation) ClearField(name string) error {
// It returns an error if the field is not defined in the schema.
func (m *ChannelMonitorHistoryMutation) ResetField(name string) error {
switch name {
case channelmonitorhistory.FieldDeletedAt:
m.ResetDeletedAt()
return nil
case channelmonitorhistory.FieldMonitorID:
m.ResetMonitorID()
return nil