Commit 325de987 by CalciumIon

feat: 渠道标签分组

parent 55f9a48f
...@@ -57,10 +57,24 @@ func GetAllChannels(c *gin.Context) { ...@@ -57,10 +57,24 @@ func GetAllChannels(c *gin.Context) {
}) })
return return
} }
tags := make(map[string]bool)
channelData := make([]*model.Channel, 0, len(channels))
for _, channel := range channels {
channelTag := channel.GetTag()
if channelTag != "" && !tags[channelTag] {
tags[channelTag] = true
tagChannels, err := model.GetChannelsByTag(channelTag)
if err == nil {
channelData = append(channelData, tagChannels...)
}
} else {
channelData = append(channelData, channel)
}
}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": true, "success": true,
"message": "", "message": "",
"data": channels, "data": channelData,
}) })
return return
} }
...@@ -279,6 +293,88 @@ func DeleteDisabledChannel(c *gin.Context) { ...@@ -279,6 +293,88 @@ func DeleteDisabledChannel(c *gin.Context) {
return return
} }
type ChannelTag struct {
Tag string `json:"tag"`
NewTag *string `json:"newTag"`
Priority *int64 `json:"priority"`
Weight *uint `json:"weight"`
}
func DisableTagChannels(c *gin.Context) {
channelTag := ChannelTag{}
err := c.ShouldBindJSON(&channelTag)
if err != nil || channelTag.Tag == "" {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "参数错误",
})
return
}
err = model.DisableChannelByTag(channelTag.Tag)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
})
return
}
func EnableTagChannels(c *gin.Context) {
channelTag := ChannelTag{}
err := c.ShouldBindJSON(&channelTag)
if err != nil || channelTag.Tag == "" {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "参数错误",
})
return
}
err = model.EnableChannelByTag(channelTag.Tag)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
})
return
}
func EditTagChannels(c *gin.Context) {
channelTag := ChannelTag{}
err := c.ShouldBindJSON(&channelTag)
if err != nil || channelTag.Tag == "" {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "参数错误",
})
return
}
err = model.EditChannelByTag(channelTag.Tag, channelTag.NewTag, channelTag.Priority, channelTag.Weight)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
})
return
}
type ChannelBatch struct { type ChannelBatch struct {
Ids []int `json:"ids"` Ids []int `json:"ids"`
} }
......
...@@ -10,12 +10,13 @@ import ( ...@@ -10,12 +10,13 @@ import (
) )
type Ability struct { type Ability struct {
Group string `json:"group" gorm:"type:varchar(64);primaryKey;autoIncrement:false"` Group string `json:"group" gorm:"type:varchar(64);primaryKey;autoIncrement:false"`
Model string `json:"model" gorm:"type:varchar(64);primaryKey;autoIncrement:false"` Model string `json:"model" gorm:"type:varchar(64);primaryKey;autoIncrement:false"`
ChannelId int `json:"channel_id" gorm:"primaryKey;autoIncrement:false;index"` ChannelId int `json:"channel_id" gorm:"primaryKey;autoIncrement:false;index"`
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
Priority *int64 `json:"priority" gorm:"bigint;default:0;index"` Priority *int64 `json:"priority" gorm:"bigint;default:0;index"`
Weight uint `json:"weight" gorm:"default:0;index"` Weight uint `json:"weight" gorm:"default:0;index"`
Tag *string `json:"tag" gorm:"index"`
} }
func GetGroupModels(group string) []string { func GetGroupModels(group string) []string {
...@@ -149,6 +150,7 @@ func (channel *Channel) AddAbilities() error { ...@@ -149,6 +150,7 @@ func (channel *Channel) AddAbilities() error {
Enabled: channel.Status == common.ChannelStatusEnabled, Enabled: channel.Status == common.ChannelStatusEnabled,
Priority: channel.Priority, Priority: channel.Priority,
Weight: uint(channel.GetWeight()), Weight: uint(channel.GetWeight()),
Tag: channel.Tag,
} }
abilities = append(abilities, ability) abilities = append(abilities, ability)
} }
...@@ -190,6 +192,24 @@ func UpdateAbilityStatus(channelId int, status bool) error { ...@@ -190,6 +192,24 @@ func UpdateAbilityStatus(channelId int, status bool) error {
return DB.Model(&Ability{}).Where("channel_id = ?", channelId).Select("enabled").Update("enabled", status).Error return DB.Model(&Ability{}).Where("channel_id = ?", channelId).Select("enabled").Update("enabled", status).Error
} }
func UpdateAbilityStatusByTag(tag string, status bool) error {
return DB.Model(&Ability{}).Where("tag = ?", tag).Select("enabled").Update("enabled", status).Error
}
func UpdateAbilityByTag(tag string, newTag *string, priority *int64, weight *uint) error {
ability := Ability{}
if newTag != nil {
ability.Tag = newTag
}
if priority != nil {
ability.Priority = priority
}
if weight != nil {
ability.Weight = *weight
}
return DB.Model(&Ability{}).Where("tag = ?", tag).Updates(ability).Error
}
func FixAbility() (int, error) { func FixAbility() (int, error) {
var channelIds []int var channelIds []int
count := 0 count := 0
......
...@@ -32,6 +32,7 @@ type Channel struct { ...@@ -32,6 +32,7 @@ type Channel struct {
Priority *int64 `json:"priority" gorm:"bigint;default:0"` Priority *int64 `json:"priority" gorm:"bigint;default:0"`
AutoBan *int `json:"auto_ban" gorm:"default:1"` AutoBan *int `json:"auto_ban" gorm:"default:1"`
OtherInfo string `json:"other_info"` OtherInfo string `json:"other_info"`
Tag *string `json:"tag" gorm:"index"`
} }
func (channel *Channel) GetModels() []string { func (channel *Channel) GetModels() []string {
...@@ -61,6 +62,17 @@ func (channel *Channel) SetOtherInfo(otherInfo map[string]interface{}) { ...@@ -61,6 +62,17 @@ func (channel *Channel) SetOtherInfo(otherInfo map[string]interface{}) {
channel.OtherInfo = string(otherInfoBytes) channel.OtherInfo = string(otherInfoBytes)
} }
func (channel *Channel) GetTag() string {
if channel.Tag == nil {
return ""
}
return *channel.Tag
}
func (channel *Channel) SetTag(tag string) {
channel.Tag = &tag
}
func (channel *Channel) GetAutoBan() bool { func (channel *Channel) GetAutoBan() bool {
if channel.AutoBan == nil { if channel.AutoBan == nil {
return false return false
...@@ -87,6 +99,12 @@ func GetAllChannels(startIdx int, num int, selectAll bool, idSort bool) ([]*Chan ...@@ -87,6 +99,12 @@ func GetAllChannels(startIdx int, num int, selectAll bool, idSort bool) ([]*Chan
return channels, err return channels, err
} }
func GetChannelsByTag(tag string) ([]*Channel, error) {
var channels []*Channel
err := DB.Where("tag = ?", tag).Find(&channels).Error
return channels, err
}
func SearchChannels(keyword string, group string, model string) ([]*Channel, error) { func SearchChannels(keyword string, group string, model string) ([]*Channel, error) {
var channels []*Channel var channels []*Channel
keyCol := "`key`" keyCol := "`key`"
...@@ -288,6 +306,42 @@ func UpdateChannelStatusById(id int, status int, reason string) { ...@@ -288,6 +306,42 @@ func UpdateChannelStatusById(id int, status int, reason string) {
} }
func EnableChannelByTag(tag string) error {
err := DB.Model(&Channel{}).Where("tag = ?", tag).Update("status", common.ChannelStatusEnabled).Error
if err != nil {
return err
}
err = UpdateAbilityStatusByTag(tag, true)
return err
}
func DisableChannelByTag(tag string) error {
err := DB.Model(&Channel{}).Where("tag = ?", tag).Update("status", common.ChannelStatusManuallyDisabled).Error
if err != nil {
return err
}
err = UpdateAbilityStatusByTag(tag, false)
return err
}
func EditChannelByTag(tag string, newTag *string, priority *int64, weight *uint) error {
updateData := Channel{}
if newTag != nil {
updateData.Tag = newTag
}
if priority != nil {
updateData.Priority = priority
}
if weight != nil {
updateData.Weight = weight
}
err := DB.Model(&Channel{}).Where("tag = ?", tag).Updates(updateData).Error
if err != nil {
return err
}
return UpdateAbilityByTag(tag, newTag, priority, weight)
}
func UpdateChannelUsedQuota(id int, quota int) { func UpdateChannelUsedQuota(id int, quota int) {
if common.BatchUpdateEnabled { if common.BatchUpdateEnabled {
addNewRecord(BatchUpdateTypeChannelUsedQuota, id, quota) addNewRecord(BatchUpdateTypeChannelUsedQuota, id, quota)
......
...@@ -91,6 +91,9 @@ func SetApiRouter(router *gin.Engine) { ...@@ -91,6 +91,9 @@ func SetApiRouter(router *gin.Engine) {
channelRoute.POST("/", controller.AddChannel) channelRoute.POST("/", controller.AddChannel)
channelRoute.PUT("/", controller.UpdateChannel) channelRoute.PUT("/", controller.UpdateChannel)
channelRoute.DELETE("/disabled", controller.DeleteDisabledChannel) channelRoute.DELETE("/disabled", controller.DeleteDisabledChannel)
channelRoute.POST("/tag/disabled", controller.DisableTagChannels)
channelRoute.POST("/tag/enabled", controller.EnableTagChannels)
channelRoute.PUT("/tag", controller.EditTagChannels)
channelRoute.DELETE("/:id", controller.DeleteChannel) channelRoute.DELETE("/:id", controller.DeleteChannel)
channelRoute.POST("/batch", controller.DeleteChannelBatch) channelRoute.POST("/batch", controller.DeleteChannelBatch)
channelRoute.POST("/fix", controller.FixChannelsAbilities) channelRoute.POST("/fix", controller.FixChannelsAbilities)
......
import { Input, Typography } from '@douyinfe/semi-ui';
import React from 'react';
const TextInput = ({ label, name, value, onChange, placeholder, type = 'text' }) => {
return (
<>
<div style={{ marginTop: 10 }}>
<Typography.Text strong>{label}</Typography.Text>
</div>
<Input
name={name}
placeholder={placeholder}
onChange={(value) => onChange(value)}
value={value}
autoComplete="new-password"
/>
</>
);
}
export default TextInput;
\ No newline at end of file
...@@ -67,6 +67,8 @@ export function renderQuotaNumberWithDigit(num, digits = 2) { ...@@ -67,6 +67,8 @@ export function renderQuotaNumberWithDigit(num, digits = 2) {
} }
export function renderNumberWithPoint(num) { export function renderNumberWithPoint(num) {
if (num === undefined)
return '';
num = num.toFixed(2); num = num.toFixed(2);
if (num >= 100000) { if (num >= 100000) {
// Convert number to string to manipulate it // Convert number to string to manipulate it
......
import React, { useState, useEffect } from 'react';
import { API, showError, showSuccess } from '../../helpers';
import { SideSheet, Space, Button, Input, Typography, Spin, Modal } from '@douyinfe/semi-ui';
import TextInput from '../../components/TextInput.js';
const EditTagModal = (props) => {
const { visible, tag, handleClose, refresh } = props;
const [loading, setLoading] = useState(false);
const originInputs = {
tag: '',
newTag: null,
}
const [inputs, setInputs] = useState(originInputs);
const handleSave = async () => {
setLoading(true);
let data = {
tag: tag,
}
let shouldSave = true;
if (inputs.newTag === tag) {
setLoading(false);
return;
}
data.newTag = inputs.newTag;
if (data.newTag === '') {
Modal.confirm({
title: '解散标签',
content: '确定要解散标签吗?',
onCancel: () => {
setLoading(false);
},
onOk: async () => {
await submit(data);
}
});
} else {
await submit(data);
}
setLoading(false);
};
const submit = async (data) => {
try {
const res = await API.put('/api/channel/tag', data);
if (res?.data?.success) {
showSuccess('标签更新成功!');
refresh();
handleClose();
}
} catch (error) {
showError(error);
}
}
useEffect(() => {
setInputs({
...originInputs,
tag: tag,
newTag: tag,
})
}, [visible]);
return (
<SideSheet
title="编辑标签"
visible={visible}
onCancel={handleClose}
footer={
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Space>
<Button onClick={handleClose}>取消</Button>
<Button type="primary" onClick={handleSave} loading={loading}>保存</Button>
</Space>
</div>
}
>
<Spin spinning={loading}>
<TextInput
label="新标签(留空则解散标签,不会删除标签下的渠道)"
name="newTag"
value={inputs.newTag}
onChange={(value) => setInputs({ ...inputs, newTag: value })}
placeholder="请输入新标签"
/>
</Spin>
</SideSheet>
);
};
export default EditTagModal;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment