操作日志中文化与功能优化

- 后端操作描述统一使用中文冒号(:)替换英文冒号(:)
- 前端资源名称列 ID 显示改为中文冒号
- 清理日志改为弹窗内独立日期选择器,不再依赖筛选条件
- 侧边栏操作记录移至渠道管理上方
- RPM/TPM 卡片标签改为中文显示
This commit is contained in:
wangxiaoji 2026-07-01 16:03:03 +08:00
parent 006e723ac5
commit 01900f1a3a
10 changed files with 63 additions and 34 deletions

View File

@ -1348,7 +1348,7 @@ func AddChannel(c *gin.Context) {
if addChannelRequest.Channel != nil {
channelName = addChannelRequest.Channel.Name
}
service.RecordCreateOperation(c, "channel", 0, channelName, "创建渠道: "+channelName, "")
service.RecordCreateOperation(c, "channel", 0, channelName, "创建渠道"+channelName, "")
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
@ -1366,7 +1366,7 @@ func DeleteChannel(c *gin.Context) {
}
model.InitChannelCache()
// 记录操作日志
service.RecordDeleteOperation(c, "channel", id, "", fmt.Sprintf("删除渠道 (ID: %d)", id), "")
service.RecordDeleteOperation(c, "channel", id, "", fmt.Sprintf("删除渠道 (ID%d)", id), "")
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
@ -1721,7 +1721,7 @@ func UpdateChannel(c *gin.Context) {
channel.Key = ""
clearChannelInfo(&channel.Channel)
// 记录操作日志
service.RecordUpdateOperation(c, "channel", channel.Id, channel.Name, "更新渠道: "+channel.Name, "")
service.RecordUpdateOperation(c, "channel", channel.Id, channel.Name, "更新渠道"+channel.Name, "")
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",

View File

@ -270,7 +270,7 @@ func CreateModelMeta(c *gin.Context) {
return
}
model.RefreshPricing()
service.RecordCreateOperation(c, "model", m.Id, m.ModelName, "创建模型: "+m.ModelName, "")
service.RecordCreateOperation(c, "model", m.Id, m.ModelName, "创建模型"+m.ModelName, "")
common.ApiSuccess(c, &m)
}
@ -319,7 +319,7 @@ func UpdateModelMeta(c *gin.Context) {
}
}
model.RefreshPricing()
service.RecordUpdateOperation(c, "model", m.Id, m.ModelName, "更新模型: "+m.ModelName, "")
service.RecordUpdateOperation(c, "model", m.Id, m.ModelName, "更新模型"+m.ModelName, "")
common.ApiSuccess(c, &m)
}
@ -336,7 +336,7 @@ func DeleteModelMeta(c *gin.Context) {
return
}
model.RefreshPricing()
service.RecordDeleteOperation(c, "model", id, "", fmt.Sprintf("删除模型 (ID: %d)", id), "")
service.RecordDeleteOperation(c, "model", id, "", fmt.Sprintf("删除模型 (ID%d)", id), "")
common.ApiSuccess(c, nil)
}

View File

@ -690,10 +690,10 @@ func UpdateOption(c *gin.Context) {
if modelPricingOptionKeys[option.Key] {
changes := computeModelPricingDiff(oldVal, valStr)
if len(changes) > 0 {
service.RecordUpdateWithDiff(c, "setting", 0, option.Key, "更新系统设置: "+option.Key, changes, "")
service.RecordUpdateWithDiff(c, "setting", 0, option.Key, "更新系统设置"+option.Key, changes, "")
}
} else {
service.RecordUpdateOperation(c, "setting", 0, option.Key, "更新系统设置: "+option.Key, "")
service.RecordUpdateOperation(c, "setting", 0, option.Key, "更新系统设置"+option.Key, "")
}
c.JSON(http.StatusOK, gin.H{

View File

@ -106,7 +106,7 @@ func AddRedemption(c *gin.Context) {
}
keys = append(keys, key)
}
service.RecordCreateOperation(c, "redemption", 0, redemption.Name, "创建兑换码: "+redemption.Name, "")
service.RecordCreateOperation(c, "redemption", 0, redemption.Name, "创建兑换码"+redemption.Name, "")
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
@ -122,7 +122,7 @@ func DeleteRedemption(c *gin.Context) {
common.ApiError(c, err)
return
}
service.RecordDeleteOperation(c, "redemption", id, "", fmt.Sprintf("删除兑换码 (ID: %d)", id), "")
service.RecordDeleteOperation(c, "redemption", id, "", fmt.Sprintf("删除兑换码 (ID%d)", id), "")
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
@ -161,7 +161,7 @@ func UpdateRedemption(c *gin.Context) {
common.ApiError(c, err)
return
}
service.RecordUpdateOperation(c, "redemption", cleanRedemption.Id, cleanRedemption.Name, "更新兑换码: "+cleanRedemption.Name, "")
service.RecordUpdateOperation(c, "redemption", cleanRedemption.Id, cleanRedemption.Name, "更新兑换码"+cleanRedemption.Name, "")
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",

View File

@ -228,7 +228,7 @@ func AddToken(c *gin.Context) {
common.ApiError(c, err)
return
}
service.RecordCreateOperation(c, "token", cleanToken.Id, cleanToken.Name, "创建令牌: "+cleanToken.Name, "")
service.RecordCreateOperation(c, "token", cleanToken.Id, cleanToken.Name, "创建令牌"+cleanToken.Name, "")
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
@ -243,7 +243,7 @@ func DeleteToken(c *gin.Context) {
common.ApiError(c, err)
return
}
service.RecordDeleteOperation(c, "token", id, "", fmt.Sprintf("删除令牌 (ID: %d)", id), "")
service.RecordDeleteOperation(c, "token", id, "", fmt.Sprintf("删除令牌 (ID%d)", id), "")
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
@ -308,7 +308,7 @@ func UpdateToken(c *gin.Context) {
common.ApiError(c, err)
return
}
service.RecordUpdateOperation(c, "token", cleanToken.Id, cleanToken.Name, "更新令牌: "+cleanToken.Name, "")
service.RecordUpdateOperation(c, "token", cleanToken.Id, cleanToken.Name, "更新令牌"+cleanToken.Name, "")
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",

View File

@ -1206,7 +1206,7 @@ func UpdateUser(c *gin.Context) {
if originUser.DistributorCommissionBps != updatedUser.DistributorCommissionBps {
changes = append(changes, service.FieldChange{Field: "分销佣金比例", OldValue: fmt.Sprintf("%d‱", originUser.DistributorCommissionBps), NewValue: fmt.Sprintf("%d‱", updatedUser.DistributorCommissionBps)})
}
service.RecordUpdateWithDiff(c, "user", updatedUser.Id, updatedUser.Username, "更新用户: "+updatedUser.Username, changes, "")
service.RecordUpdateWithDiff(c, "user", updatedUser.Id, updatedUser.Username, "更新用户"+updatedUser.Username, changes, "")
c.JSON(http.StatusOK, gin.H{
"success": true,
@ -1246,7 +1246,7 @@ func AdminClearUserBinding(c *gin.Context) {
}
model.RecordLog(user.Id, model.LogTypeManage, fmt.Sprintf("admin cleared %s binding for user %s", bindingType, user.Username))
service.RecordDeleteOperation(c, "user_binding", user.Id, user.Username, "清除用户绑定["+bindingType+"]: "+user.Username, "")
service.RecordDeleteOperation(c, "user_binding", user.Id, user.Username, "清除用户绑定["+bindingType+"]"+user.Username, "")
c.JSON(http.StatusOK, gin.H{
"success": true,
@ -1421,7 +1421,7 @@ func DeleteUser(c *gin.Context) {
})
return
}
service.RecordDeleteOperation(c, "user", id, originUser.Username, "删除用户: "+originUser.Username, "")
service.RecordDeleteOperation(c, "user", id, originUser.Username, "删除用户"+originUser.Username, "")
}
func DeleteSelf(c *gin.Context) {
@ -1501,7 +1501,7 @@ func CreateUser(c *gin.Context) {
common.ApiError(c, err)
return
}
service.RecordCreateOperation(c, "user", cleanUser.Id, cleanUser.Username, "创建用户: "+cleanUser.Username, "")
service.RecordCreateOperation(c, "user", cleanUser.Id, cleanUser.Username, "创建用户"+cleanUser.Username, "")
c.JSON(http.StatusOK, gin.H{
"success": true,
@ -1781,7 +1781,7 @@ func ManageUser(c *gin.Context) {
}
changes = append(changes, service.FieldChange{Field: "学员", OldValue: oldLabel, NewValue: newLabel})
}
service.RecordUpdateWithDiff(c, "user", user.Id, user.Username, label+": "+user.Username, changes, "")
service.RecordUpdateWithDiff(c, "user", user.Id, user.Username, label+""+user.Username, changes, "")
}
switch req.Action {

View File

@ -222,7 +222,7 @@ func commonActionLabel(action string, targetType string, targetName string) stri
}
if targetName != "" {
return actionLabel + typeLabel + ": " + targetName
return actionLabel + typeLabel + "" + targetName
}
return actionLabel + typeLabel
}

View File

@ -207,6 +207,12 @@ const SiderBar = ({ onNavigate = () => {} }) => {
const adminItems = useMemo(() => {
const items = [
{
text: t('操作记录'),
itemKey: 'operation-log',
to: '/console/operation-log',
className: isAdmin() ? '' : 'tableHiddle',
},
{
text: t('渠道管理'),
itemKey: 'channel',
@ -249,12 +255,6 @@ const SiderBar = ({ onNavigate = () => {} }) => {
to: '/user',
className: isAdmin() ? '' : 'tableHiddle',
},
{
text: t('操作记录'),
itemKey: 'operation-log',
to: '/console/operation-log',
className: isAdmin() ? '' : 'tableHiddle',
},
{
text: t('代理管理'),
itemKey: 'distributor',

View File

@ -202,7 +202,7 @@ const RechargeCard = ({
{
key: 'rpm',
icon: Gauge,
label: t('RPM'),
label: t('每分钟请求'),
value: rpmTpmData?.rpm,
sublabel: t('最近1分钟'),
raw: true,
@ -211,7 +211,7 @@ const RechargeCard = ({
{
key: 'tpm',
icon: TrendingUp,
label: t('TPM'),
label: t('每分钟Token'),
value: rpmTpmData?.tpm,
sublabel: t('最近1分钟'),
raw: true,

View File

@ -85,6 +85,10 @@ const OperationLog = () => {
const [detailVisible, setDetailVisible] = useState(false);
const [selectedLog, setSelectedLog] = useState(null);
// Cleanup modal
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
const [deleteDate, setDeleteDate] = useState(null);
const fetchLogs = async () => {
setLoading(true);
try {
@ -132,14 +136,17 @@ const OperationLog = () => {
fetchLogs();
};
const handleDeleteOldLogs = async () => {
if (!dateRange || !dateRange[1]) {
const handleDeleteOldLogs = () => {
setDeleteDate(null);
setDeleteModalVisible(true);
};
const handleConfirmDelete = () => {
if (!deleteDate) {
showError('请选择截止日期');
return;
}
const targetTimestamp = Math.floor(
new Date(dateRange[1]).getTime() / 1000
);
const targetTimestamp = Math.floor(new Date(deleteDate).getTime() / 1000);
Modal.confirm({
title: '确认清理',
content: `确定要清理 ${timestamp2string(targetTimestamp)} 之前的操作日志吗?此操作不可逆。`,
@ -151,6 +158,7 @@ const OperationLog = () => {
const { success, data, message } = res.data;
if (success) {
showSuccess(`成功清理 ${data} 条操作日志`);
setDeleteModalVisible(false);
fetchLogs();
} else {
showError(message);
@ -206,7 +214,7 @@ const OperationLog = () => {
dataIndex: 'target_name',
key: 'target_name',
width: 160,
render: (text, record) => text || `ID: ${record.target_id}`,
render: (text, record) => text || `ID${record.target_id}`,
},
{
title: t('操作描述'),
@ -339,6 +347,27 @@ const OperationLog = () => {
/>
</Card>
<Modal
title={t('清理日志')}
visible={deleteModalVisible}
onCancel={() => setDeleteModalVisible(false)}
onOk={handleConfirmDelete}
okText={t('确认清理')}
cancelText={t('取消')}
width={420}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<Text>{t('选择截止日期,将清理该日期之前的所有操作日志。此操作不可逆。')}</Text>
<DatePicker
type='dateTime'
placeholder={t('选择截止日期')}
value={deleteDate}
onChange={setDeleteDate}
style={{ width: '100%' }}
/>
</div>
</Modal>
<Modal
title={t('操作详情')}
visible={detailVisible}