fix: 站点编辑实现编辑弹窗+PUT端点, 配置查看修复(加载失败)改用/content端点+DTO加submitterId
This commit is contained in:
@@ -52,7 +52,34 @@ public class ConfigController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载指定版本配置(支持 ETag 条件请求)
|
* 查看配置内容(管理员用,返回 ApiResponse 包装)
|
||||||
|
*/
|
||||||
|
@GetMapping("/{siteId}/{version}/content")
|
||||||
|
public ApiResponse<String> viewConfigContent(
|
||||||
|
@PathVariable("siteId") String siteId,
|
||||||
|
@PathVariable("version") String version) {
|
||||||
|
|
||||||
|
SiteConfig config = configService.getConfig(siteId, version);
|
||||||
|
if (config == null) {
|
||||||
|
return ApiResponse.error(404, "配置不存在: " + siteId + " v" + version);
|
||||||
|
}
|
||||||
|
|
||||||
|
String content;
|
||||||
|
try {
|
||||||
|
content = storageService.read(config.getStoragePath());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to read config: siteId={}, version={}", siteId, version, e);
|
||||||
|
return ApiResponse.error(500, "读取配置文件失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
if (content == null) {
|
||||||
|
return ApiResponse.error(404, "配置文件不存在: " + config.getStoragePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResponse.success(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载指定版本配置(支持 ETag 条件请求,供 mediabot 客户端使用)
|
||||||
*/
|
*/
|
||||||
@GetMapping("/{siteId}/{version}")
|
@GetMapping("/{siteId}/{version}")
|
||||||
public ResponseEntity<String> downloadConfig(
|
public ResponseEntity<String> downloadConfig(
|
||||||
|
|||||||
@@ -38,6 +38,29 @@ public class SiteController {
|
|||||||
return ApiResponse.success(siteService.toDTO(siteService.findBySiteId(siteId)));
|
return ApiResponse.success(siteService.toDTO(siteService.findBySiteId(siteId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑站点基本信息(需 JWT 认证,管理员操作)
|
||||||
|
*/
|
||||||
|
@PutMapping("/{siteId}")
|
||||||
|
public ApiResponse<SiteDTO> updateSite(
|
||||||
|
@PathVariable("siteId") String siteId,
|
||||||
|
@RequestBody Map<String, Object> payload,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
|
String name = (String) payload.get("name");
|
||||||
|
String description = (String) payload.get("description");
|
||||||
|
String baseUrl = (String) payload.get("baseUrl");
|
||||||
|
String siteType = (String) payload.get("siteType");
|
||||||
|
Long operatorId = (Long) request.getAttribute("accountId");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Site site = siteService.updateSite(siteId, name, description, baseUrl, siteType, operatorId);
|
||||||
|
return ApiResponse.success(siteService.toDTO(site));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return ApiResponse.error(404, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交站点收录(需 HMAC 鉴权)
|
* 提交站点收录(需 HMAC 鉴权)
|
||||||
* 创建或更新站点信息,自动设置为活跃状态
|
* 创建或更新站点信息,自动设置为活跃状态
|
||||||
|
|||||||
@@ -19,5 +19,6 @@ public class SiteConfigDTO {
|
|||||||
private String schemaVersion;
|
private String schemaVersion;
|
||||||
private ConfigStatus status;
|
private ConfigStatus status;
|
||||||
private Boolean isLatest;
|
private Boolean isLatest;
|
||||||
|
private Long submitterId;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ public interface SiteService {
|
|||||||
Site updateSiteInfo(String siteId, String status, List<String> names,
|
Site updateSiteInfo(String siteId, String status, List<String> names,
|
||||||
List<String> urls, String notes, Long submitterId);
|
List<String> urls, String notes, Long submitterId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员编辑站点基本信息
|
||||||
|
*/
|
||||||
|
Site updateSite(String siteId, String name, String description, String baseUrl,
|
||||||
|
String siteType, Long operatorId);
|
||||||
|
|
||||||
/** 待审核站点列表 */
|
/** 待审核站点列表 */
|
||||||
List<Site> listPendingSites();
|
List<Site> listPendingSites();
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ public class ConfigServiceImpl implements ConfigService {
|
|||||||
dto.setSchemaVersion(config.getSchemaVersion());
|
dto.setSchemaVersion(config.getSchemaVersion());
|
||||||
dto.setStatus(config.getStatus());
|
dto.setStatus(config.getStatus());
|
||||||
dto.setIsLatest(config.getIsLatest());
|
dto.setIsLatest(config.getIsLatest());
|
||||||
|
dto.setSubmitterId(config.getSubmitterId());
|
||||||
dto.setCreatedAt(config.getCreatedAt());
|
dto.setCreatedAt(config.getCreatedAt());
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,6 +152,26 @@ public class SiteServiceImpl implements SiteService {
|
|||||||
return site;
|
return site;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Site updateSite(String siteId, String name, String description, String baseUrl,
|
||||||
|
String siteType, Long operatorId) {
|
||||||
|
Site site = siteMapper.selectBySiteId(siteId);
|
||||||
|
if (site == null) {
|
||||||
|
throw new IllegalArgumentException("站点不存在: " + siteId);
|
||||||
|
}
|
||||||
|
if (name != null && !name.isBlank()) site.setName(name);
|
||||||
|
if (baseUrl != null && !baseUrl.isBlank()) site.setBaseUrl(baseUrl);
|
||||||
|
if (description != null) site.setDescription(description);
|
||||||
|
if (siteType != null && !siteType.isBlank()) {
|
||||||
|
try {
|
||||||
|
site.setSiteType(SiteType.fromValue(siteType));
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
siteMapper.updateById(site);
|
||||||
|
log.info("站点编辑: siteId={} name={}", siteId, name);
|
||||||
|
return site;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Site> listPendingSites() {
|
public List<Site> listPendingSites() {
|
||||||
return siteMapper.selectList(
|
return siteMapper.selectList(
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export default {
|
|||||||
pending: () => api.get('/configs/pending'),
|
pending: () => api.get('/configs/pending'),
|
||||||
bySite: (siteId) => api.get(`/configs/${siteId}`),
|
bySite: (siteId) => api.get(`/configs/${siteId}`),
|
||||||
download: (siteId, version) => api.get(`/configs/${siteId}/${version}`),
|
download: (siteId, version) => api.get(`/configs/${siteId}/${version}`),
|
||||||
|
content: (siteId, version) => api.get(`/configs/${siteId}/${version}/content`),
|
||||||
approve: (id, comment) => api.post(`/admin/reviews/${id}/approve`, null, { params: { comment } }),
|
approve: (id, comment) => api.post(`/admin/reviews/${id}/approve`, null, { params: { comment } }),
|
||||||
reject: (id, comment) => api.post(`/admin/reviews/${id}/reject`, null, { params: { comment } })
|
reject: (id, comment) => api.post(`/admin/reviews/${id}/reject`, null, { params: { comment } })
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -81,19 +81,29 @@ const handleView = async (row) => {
|
|||||||
showHistory.value = false
|
showHistory.value = false
|
||||||
viewVisible.value = true
|
viewVisible.value = true
|
||||||
try {
|
try {
|
||||||
const res = await api.configs.download(row.siteId, row.version)
|
const resp = await api.configs.content(row.siteId, row.version)
|
||||||
configContent.value = typeof res === 'string' ? res : JSON.stringify(res, null, 2)
|
const content = resp.data
|
||||||
} catch {
|
if (content) {
|
||||||
configContent.value = '(加载失败)'
|
configContent.value = typeof content === 'string' ? content : JSON.stringify(content, null, 2)
|
||||||
|
} else {
|
||||||
|
configContent.value = '(无配置内容)'
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
configContent.value = '(加载失败: ' + (e.response?.data?.message || e.message || '未知错误') + ')'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadHistoryConfig = async (h) => {
|
const loadHistoryConfig = async (h) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.configs.download(h.siteId, h.version)
|
const resp = await api.configs.content(h.siteId, h.version)
|
||||||
configContent.value = typeof res === 'string' ? res : JSON.stringify(res, null, 2)
|
const content = resp.data
|
||||||
} catch {
|
if (content) {
|
||||||
configContent.value = '(加载失败)'
|
configContent.value = typeof content === 'string' ? content : JSON.stringify(content, null, 2)
|
||||||
|
} else {
|
||||||
|
configContent.value = '(无配置内容)'
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
configContent.value = '(加载失败: ' + (e.response?.data?.message || e.message || '未知错误') + ')'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,31 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 编辑对话框 -->
|
||||||
|
<el-dialog v-model="editVisible" title="编辑站点" width="500px" :close-on-click-modal="false">
|
||||||
|
<el-form :model="editForm" label-width="80px" @submit.prevent="handleSave">
|
||||||
|
<el-form-item label="站点ID">
|
||||||
|
<el-input v-model="editForm.siteId" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="名称">
|
||||||
|
<el-input v-model="editForm.name" placeholder="站点名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="基础URL">
|
||||||
|
<el-input v-model="editForm.baseUrl" placeholder="https://example.com" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="类型">
|
||||||
|
<el-input v-model="editForm.siteType" placeholder="nexusphp / mteam / ..." />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="描述">
|
||||||
|
<el-input v-model="editForm.description" type="textarea" :rows="3" placeholder="站点描述" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="editVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSave" :loading="saving">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -30,6 +55,9 @@ import api from '../api'
|
|||||||
|
|
||||||
const sites = ref([])
|
const sites = ref([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const editVisible = ref(false)
|
||||||
|
const saving = ref(false)
|
||||||
|
const editForm = ref({ id: null, siteId: '', name: '', baseUrl: '', siteType: '', description: '' })
|
||||||
|
|
||||||
const fetchSites = async () => {
|
const fetchSites = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -44,7 +72,38 @@ const fetchSites = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleEdit = (row) => {
|
const handleEdit = (row) => {
|
||||||
ElMessage.info(`编辑站点: ${row.name}`)
|
editForm.value = {
|
||||||
|
id: row.id,
|
||||||
|
siteId: row.siteId,
|
||||||
|
name: row.name || '',
|
||||||
|
baseUrl: row.baseUrl || '',
|
||||||
|
siteType: row.siteType || '',
|
||||||
|
description: row.description || '',
|
||||||
|
}
|
||||||
|
editVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSave = async () => {
|
||||||
|
if (!editForm.value.name.trim() || !editForm.value.baseUrl.trim()) {
|
||||||
|
ElMessage.warning('名称和基础URL不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
saving.value = true
|
||||||
|
try {
|
||||||
|
await api.sites.update(editForm.value.siteId, {
|
||||||
|
name: editForm.value.name.trim(),
|
||||||
|
baseUrl: editForm.value.baseUrl.trim(),
|
||||||
|
siteType: editForm.value.siteType.trim() || 'unknown',
|
||||||
|
description: editForm.value.description.trim() || null,
|
||||||
|
})
|
||||||
|
ElMessage.success('保存成功')
|
||||||
|
editVisible.value = false
|
||||||
|
fetchSites()
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error(e.response?.data?.message || '保存失败')
|
||||||
|
} finally {
|
||||||
|
saving.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(fetchSites)
|
onMounted(fetchSites)
|
||||||
|
|||||||
Reference in New Issue
Block a user