feat(site): integrate multilingual content management / 集成多语言内容管理

完善官网、管理后台与后端的多语言内容编辑和发布流程,调整站点页面及相关接口,并同步数据库脚本。

新增容器部署配置,改用环境变量注入数据库凭据;补充仓库忽略规则,并停止跟踪本地环境文件和 Maven 构建产物。
This commit is contained in:
liuwenxin committed 2026-09-10 14:46:06 +08:00
1 parent 4ee6e9ae83
commit 85083ab962
1056 files changed
+15449 -132933

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
# 使用 JDK8 镜像(RuoYi 默认用 8,如果是 17 请换成 openjdk:17-jdk-alpine)
FROM eclipse-temurin:17-jdk-alpine
WORKDIR /app
# 复制本地已经打包好的 jar 包 (前提是你已经在本地执行过 mvn clean package)
COPY ruoyi-admin/target/ruoyi-admin.jar app.jar
EXPOSE 7003
ENTRYPOINT ["java", "-jar", "app.jar"]
@@ -18,6 +18,7 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.file.ImageUploadUtils;
import com.ruoyi.framework.config.ServerConfig;
/**
@@ -79,7 +80,7 @@ public class CommonController
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath();
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String fileName = upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
AjaxResult ajax = AjaxResult.success();
ajax.put("url", url);
@@ -111,7 +112,7 @@ public class CommonController
for (MultipartFile file : files)
{
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String fileName = upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
urls.add(url);
fileNames.add(fileName);
@@ -159,4 +160,10 @@ public class CommonController
log.error("下载文件失败", e);
}
}
private String upload(String filePath, MultipartFile file) throws Exception
{
return ImageUploadUtils.isImage(file) ? ImageUploadUtils.upload(filePath, file)
: FileUploadUtils.upload(filePath, file);
}
}
@@ -0,0 +1,87 @@
package com.ruoyi.web.controller.common;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.config.RuoYiConfig;
/**
* Handles uploaded portal assets before the generic static resource handler.
*/
@RestController
public class ProfileUploadController
{
private static final String PROFILE_PREFIX = "/profile/";
private static final byte[] MISSING_IMAGE_BYTES = Base64.getDecoder().decode(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=");
@RequestMapping(value = "/profile/upload/**", method = { RequestMethod.GET, RequestMethod.HEAD })
public ResponseEntity<Resource> getUploadResource(HttpServletRequest request) throws IOException
{
String requestUri = request.getRequestURI();
String relativePath = requestUri.startsWith(PROFILE_PREFIX) ? requestUri.substring(PROFILE_PREFIX.length()) : "";
relativePath = URLDecoder.decode(relativePath, StandardCharsets.UTF_8);
Path root = Path.of(RuoYiConfig.getProfile()).toAbsolutePath().normalize();
Path target = root.resolve(relativePath).normalize();
if (target.startsWith(root) && Files.isRegularFile(target) && Files.isReadable(target))
{
MediaType mediaType = resolveMediaType(target);
return ResponseEntity.ok()
.contentType(mediaType)
.cacheControl(CacheControl.noCache())
.body(new FileSystemResource(target));
}
if (isImagePath(relativePath))
{
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
.cacheControl(CacheControl.noStore())
.body(new ByteArrayResource(MISSING_IMAGE_BYTES));
}
return ResponseEntity.notFound().build();
}
private static MediaType resolveMediaType(Path path)
{
try
{
String contentType = Files.probeContentType(path);
if (contentType != null && !contentType.isBlank())
{
return MediaType.parseMediaType(contentType);
}
}
catch (IOException | IllegalArgumentException ignored)
{
}
return MediaType.APPLICATION_OCTET_STREAM;
}
private static boolean isImagePath(String path)
{
if (path == null)
{
return false;
}
String lowerPath = path.toLowerCase();
return lowerPath.endsWith(".png") || lowerPath.endsWith(".jpg") || lowerPath.endsWith(".jpeg")
|| lowerPath.endsWith(".gif") || lowerPath.endsWith(".webp") || lowerPath.endsWith(".svg")
|| lowerPath.endsWith(".ico");
}
}
@@ -20,7 +20,7 @@ import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.ImageUploadUtils;
import com.ruoyi.framework.config.ServerConfig;
import com.ruoyi.system.domain.HuayangCompanyHonor;
import com.ruoyi.system.service.IHuayangCompanyHonorService;
@@ -113,7 +113,7 @@ public class HuayangCompanyHonorController extends BaseController
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath() + "/huayang/honor";
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String fileName = ImageUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
AjaxResult ajax = AjaxResult.success();
ajax.put("url", url);
@@ -125,4 +125,4 @@ public class HuayangCompanyHonorController extends BaseController
return error("上传失败:" + e.getMessage());
}
}
}
}
@@ -47,7 +47,7 @@ public class HuayangCompanySectionController extends BaseController
/**
* 按板块标识查询(用于后台快速打开 about_intro 等,须在 /{sectionId} 之前声明)
*/
@PreAuthorize("@ss.hasAnyPermi('huayang:companySection:query,huayang:joinPageContent:query')")
@PreAuthorize("@ss.hasPermi('huayang:companySection:query')")
@GetMapping("/byCode/{sectionCode}")
public AjaxResult getBySectionCode(@PathVariable("sectionCode") String sectionCode)
{
@@ -67,7 +67,7 @@ public class HuayangCompanySectionController extends BaseController
/**
* 新增公司简介
*/
@PreAuthorize("@ss.hasAnyPermi('huayang:companySection:add,huayang:joinPageContent:edit')")
@PreAuthorize("@ss.hasPermi('huayang:companySection:add')")
@Log(title = "公司简介", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody HuayangCompanySection huayangCompanySection)
@@ -79,7 +79,7 @@ public class HuayangCompanySectionController extends BaseController
/**
* 修改公司简介
*/
@PreAuthorize("@ss.hasAnyPermi('huayang:companySection:edit,huayang:joinPageContent:edit')")
@PreAuthorize("@ss.hasPermi('huayang:companySection:edit')")
@Log(title = "公司简介", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody HuayangCompanySection huayangCompanySection)
@@ -1,91 +0,0 @@
package com.ruoyi.web.controller.huayang;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.HuayangCompanyTech;
import com.ruoyi.system.service.IHuayangCompanyTechService;
/**
* 品质技术管理Controller
*
* @author huayang
*/
@RestController
@RequestMapping("/huayang/companyTech")
public class HuayangCompanyTechController extends BaseController
{
@Autowired
private IHuayangCompanyTechService huayangCompanyTechService;
/**
* 查询品质技术列表
*/
@PreAuthorize("@ss.hasPermi('huayang:companyTech:list')")
@GetMapping("/list")
public TableDataInfo list(HuayangCompanyTech huayangCompanyTech)
{
startPage();
List<HuayangCompanyTech> list = huayangCompanyTechService.selectHuayangCompanyTechList(huayangCompanyTech);
return getDataTable(list);
}
/**
* 获取品质技术详情
*/
@PreAuthorize("@ss.hasPermi('huayang:companyTech:query')")
@GetMapping(value = "/{techId}")
public AjaxResult getInfo(@PathVariable("techId") Long techId)
{
return success(huayangCompanyTechService.selectHuayangCompanyTechByTechId(techId));
}
/**
* 新增品质技术
*/
@PreAuthorize("@ss.hasPermi('huayang:companyTech:add')")
@Log(title = "品质技术", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody HuayangCompanyTech huayangCompanyTech)
{
huayangCompanyTech.setCreateBy(getUsername());
return toAjax(huayangCompanyTechService.insertHuayangCompanyTech(huayangCompanyTech));
}
/**
* 修改品质技术
*/
@PreAuthorize("@ss.hasPermi('huayang:companyTech:edit')")
@Log(title = "品质技术", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody HuayangCompanyTech huayangCompanyTech)
{
huayangCompanyTech.setUpdateBy(getUsername());
return toAjax(huayangCompanyTechService.updateHuayangCompanyTech(huayangCompanyTech));
}
/**
* 删除品质技术
*/
@PreAuthorize("@ss.hasPermi('huayang:companyTech:remove')")
@Log(title = "品质技术", businessType = BusinessType.DELETE)
@DeleteMapping("/{techIds}")
public AjaxResult remove(@PathVariable Long[] techIds)
{
return toAjax(huayangCompanyTechService.deleteHuayangCompanyTechByTechIds(techIds));
}
}
@@ -1,60 +0,0 @@
package com.ruoyi.web.controller.huayang;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.HuayangJoinApply;
import com.ruoyi.system.service.IHuayangJoinApplyService;
/**
* 招商申请管理端Controller
*
* @author huayang
*/
@RestController
@RequestMapping("/huayang/joinApply")
public class HuayangJoinApplyController extends BaseController
{
@Autowired
private IHuayangJoinApplyService joinApplyService;
@PreAuthorize("@ss.hasPermi('huayang:joinApply:list')")
@GetMapping("/list")
public TableDataInfo list(HuayangJoinApply apply)
{
startPage();
List<HuayangJoinApply> list = joinApplyService.selectHuayangJoinApplyList(apply);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('huayang:joinApply:query')")
@GetMapping(value = "/{applyId}")
public AjaxResult getInfo(@PathVariable("applyId") Long applyId)
{
return success(joinApplyService.selectHuayangJoinApplyById(applyId));
}
@PreAuthorize("@ss.hasPermi('huayang:joinApply:remove')")
@Log(title = "招商申请", businessType = BusinessType.DELETE)
@DeleteMapping("/{applyIds}")
public AjaxResult remove(@PathVariable Long[] applyIds)
{
return toAjax(joinApplyService.deleteHuayangJoinApplyByIds(applyIds));
}
@PreAuthorize("@ss.hasPermi('huayang:joinApply:handle')")
@Log(title = "处理招商申请", businessType = BusinessType.UPDATE)
@PutMapping("/handle")
public AjaxResult handle(@Validated @RequestBody HuayangJoinApply apply)
{
apply.setUpdateBy(getUsername());
return toAjax(joinApplyService.handleApply(apply));
}
}
@@ -1,9 +1,14 @@
package com.ruoyi.web.controller.huayang;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.apache.commons.lang3.StringUtils;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
@@ -21,6 +26,8 @@ import com.ruoyi.system.service.IHuayangI18nService;
@RequestMapping("/huayang")
public class HuayangLanguageController extends BaseController
{
private static final Pattern I18N_KEY_PATTERN = Pattern.compile("^[a-zA-Z0-9_.-]{2,100}$");
@Autowired
private IHuayangLanguageService languageService;
@@ -84,4 +91,277 @@ public class HuayangLanguageController extends BaseController
{
return toAjax(i18nService.deleteHuayangI18nByIds(i18nIds));
}
/**
* 批量导入翻译(支持草稿模式、统计结果、逐条错误信息)
*/
@PreAuthorize("@ss.hasPermi('huayang:translation:add')")
@PostMapping("/translation/import")
public AjaxResult importTranslation(@RequestBody TranslationImportRequest request)
{
if (request == null || request.getItems() == null || request.getItems().isEmpty())
{
return error("导入数据不能为空");
}
String defaultStatus = StringUtils.defaultIfBlank(request.getDefaultStatus(), "0");
if (!"0".equals(defaultStatus) && !"1".equals(defaultStatus))
{
return error("默认发布状态仅支持 0(草稿) 或 1(已发布)");
}
int total = request.getItems().size();
int inserted = 0;
int updated = 0;
int skipped = 0;
List<String> errors = new ArrayList<>();
int index = 0;
for (TranslationImportItem item : request.getItems())
{
index++;
String rowPrefix = "第" + index + "行";
if (item == null)
{
skipped++;
errors.add(rowPrefix + ":空记录已跳过");
continue;
}
String langCode = StringUtils.lowerCase(StringUtils.trimToEmpty(item.getLangCode()));
String pageModule = StringUtils.trimToEmpty(item.getPageModule());
String i18nKey = StringUtils.trimToEmpty(item.getI18nKey());
String i18nValue = item.getI18nValue() == null ? "" : item.getI18nValue();
String fallbackLangCode = StringUtils.lowerCase(StringUtils.trimToEmpty(item.getFallbackLangCode()));
String translateStatus = StringUtils.trimToEmpty(item.getTranslateStatus());
if (StringUtils.isBlank(langCode) || StringUtils.isBlank(i18nKey))
{
skipped++;
errors.add(rowPrefix + ":langCode / i18nKey 不能为空");
continue;
}
if (!I18N_KEY_PATTERN.matcher(i18nKey).matches())
{
skipped++;
errors.add(rowPrefix + ":i18nKey 格式非法(仅支持字母数字._-,长度2-100)");
continue;
}
if (StringUtils.isBlank(pageModule))
{
pageModule = "common";
}
if (StringUtils.isBlank(fallbackLangCode))
{
fallbackLangCode = "zh_cn";
}
if (StringUtils.isBlank(translateStatus))
{
translateStatus = defaultStatus;
}
if (!"0".equals(translateStatus) && !"1".equals(translateStatus))
{
skipped++;
errors.add(rowPrefix + ":translateStatus 仅支持 0/1");
continue;
}
HuayangI18n exists = i18nService.selectHuayangI18nByKey(langCode, i18nKey);
if (exists != null)
{
exists.setI18nValue(i18nValue);
exists.setPageModule(pageModule);
exists.setFallbackLangCode(fallbackLangCode);
exists.setTranslateStatus(translateStatus);
exists.setUpdateBy(getUsername());
i18nService.updateHuayangI18n(exists);
updated++;
}
else
{
HuayangI18n record = new HuayangI18n();
record.setLangCode(langCode);
record.setPageModule(pageModule);
record.setI18nKey(i18nKey);
record.setI18nValue(i18nValue);
record.setFallbackLangCode(fallbackLangCode);
record.setTranslateStatus(translateStatus);
record.setCreateBy(getUsername());
i18nService.insertHuayangI18n(record);
inserted++;
}
}
Map<String, Object> result = new LinkedHashMap<>();
result.put("total", total);
result.put("inserted", inserted);
result.put("updated", updated);
result.put("skipped", skipped);
result.put("errors", errors);
return success(result);
}
/**
* 按筛选条件一键发布(将草稿改为已发布)
*/
@PreAuthorize("@ss.hasPermi('huayang:translation:edit')")
@PostMapping("/translation/publish")
public AjaxResult publishTranslation(@RequestBody TranslationPublishRequest request)
{
HuayangI18n query = new HuayangI18n();
if (request != null)
{
query.setLangCode(request.getLangCode());
query.setPageModule(request.getPageModule());
query.setI18nKey(request.getI18nKey());
}
List<HuayangI18n> rows = i18nService.selectHuayangI18nList(query);
int affected = 0;
for (HuayangI18n row : rows)
{
if ("1".equals(row.getTranslateStatus()))
{
continue;
}
row.setTranslateStatus("1");
row.setUpdateBy(getUsername());
i18nService.updateHuayangI18n(row);
affected++;
}
Map<String, Object> result = new LinkedHashMap<>();
result.put("matched", rows.size());
result.put("published", affected);
return success(result);
}
public static class TranslationImportRequest
{
private String defaultStatus;
private List<TranslationImportItem> items;
public String getDefaultStatus()
{
return defaultStatus;
}
public void setDefaultStatus(String defaultStatus)
{
this.defaultStatus = defaultStatus;
}
public List<TranslationImportItem> getItems()
{
return items;
}
public void setItems(List<TranslationImportItem> items)
{
this.items = items;
}
}
public static class TranslationImportItem
{
private String langCode;
private String pageModule;
private String i18nKey;
private String i18nValue;
private String fallbackLangCode;
private String translateStatus;
public String getLangCode()
{
return langCode;
}
public void setLangCode(String langCode)
{
this.langCode = langCode;
}
public String getPageModule()
{
return pageModule;
}
public void setPageModule(String pageModule)
{
this.pageModule = pageModule;
}
public String getI18nKey()
{
return i18nKey;
}
public void setI18nKey(String i18nKey)
{
this.i18nKey = i18nKey;
}
public String getI18nValue()
{
return i18nValue;
}
public void setI18nValue(String i18nValue)
{
this.i18nValue = i18nValue;
}
public String getFallbackLangCode()
{
return fallbackLangCode;
}
public void setFallbackLangCode(String fallbackLangCode)
{
this.fallbackLangCode = fallbackLangCode;
}
public String getTranslateStatus()
{
return translateStatus;
}
public void setTranslateStatus(String translateStatus)
{
this.translateStatus = translateStatus;
}
}
public static class TranslationPublishRequest
{
private String langCode;
private String pageModule;
private String i18nKey;
public String getLangCode()
{
return langCode;
}
public void setLangCode(String langCode)
{
this.langCode = langCode;
}
public String getPageModule()
{
return pageModule;
}
public void setPageModule(String pageModule)
{
this.pageModule = pageModule;
}
public String getI18nKey()
{
return i18nKey;
}
public void setI18nKey(String i18nKey)
{
this.i18nKey = i18nKey;
}
}
}
@@ -1,102 +0,0 @@
package com.ruoyi.web.controller.huayang;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysDictDataService;
/**
* 新闻类型(字典 huayang_news_type)管理,供「新闻分类」子菜单使用
*/
@RestController
@RequestMapping("/huayang/newsCategory")
public class HuayangNewsCategoryController extends BaseController
{
public static final String NEWS_TYPE_DICT = "huayang_news_type";
@Autowired
private ISysDictDataService dictDataService;
@PreAuthorize("@ss.hasPermi('huayang:newsCategory:list')")
@GetMapping("/list")
public TableDataInfo list(SysDictData query)
{
query.setDictType(NEWS_TYPE_DICT);
if (StringUtils.isNotEmpty(query.getDictLabel()))
{
query.setDictLabel(query.getDictLabel().trim());
}
startPage();
List<SysDictData> list = dictDataService.selectDictDataList(query);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('huayang:newsCategory:query')")
@GetMapping(value = "/{dictCode}")
public AjaxResult getInfo(@PathVariable Long dictCode)
{
SysDictData data = dictDataService.selectDictDataById(dictCode);
if (data == null || !NEWS_TYPE_DICT.equals(data.getDictType()))
{
return error("数据不存在或无权访问");
}
return success(data);
}
@PreAuthorize("@ss.hasPermi('huayang:newsCategory:add')")
@Log(title = "新闻类型字典", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysDictData data)
{
data.setDictType(NEWS_TYPE_DICT);
data.setCreateBy(getUsername());
return toAjax(dictDataService.insertDictData(data));
}
@PreAuthorize("@ss.hasPermi('huayang:newsCategory:edit')")
@Log(title = "新闻类型字典", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody SysDictData data)
{
SysDictData existing = dictDataService.selectDictDataById(data.getDictCode());
if (existing == null || !NEWS_TYPE_DICT.equals(existing.getDictType()))
{
return error("数据不存在或无权修改");
}
data.setDictType(NEWS_TYPE_DICT);
data.setUpdateBy(getUsername());
return toAjax(dictDataService.updateDictData(data));
}
@PreAuthorize("@ss.hasPermi('huayang:newsCategory:remove')")
@Log(title = "新闻类型字典", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictCodes}")
public AjaxResult remove(@PathVariable Long[] dictCodes)
{
for (Long dictCode : dictCodes)
{
SysDictData d = dictDataService.selectDictDataById(dictCode);
if (d == null || !NEWS_TYPE_DICT.equals(d.getDictType()))
{
return error("包含非法数据,已中止删除");
}
}
dictDataService.deleteDictDataByIds(dictCodes);
return success();
}
}
@@ -1,106 +0,0 @@
package com.ruoyi.web.controller.huayang;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.HuayangNews;
import com.ruoyi.system.service.IHuayangNewsService;
/**
* 新闻动态管理端Controller
*
* @author huayang
*/
@RestController
@RequestMapping("/huayang/news")
public class HuayangNewsController extends BaseController
{
@Autowired
private IHuayangNewsService newsService;
/**
* 查询新闻动态列表
*/
@PreAuthorize("@ss.hasPermi('huayang:news:list')")
@GetMapping("/list")
public TableDataInfo list(HuayangNews news)
{
startPage();
List<HuayangNews> list = newsService.selectHuayangNewsList(news);
return getDataTable(list);
}
/**
* 导出新闻动态列表
*/
@PreAuthorize("@ss.hasPermi('huayang:news:export')")
@Log(title = "新闻动态", businessType = BusinessType.EXPORT)
@GetMapping("/export")
public AjaxResult export(HuayangNews news)
{
List<HuayangNews> list = newsService.selectHuayangNewsList(news);
ExcelUtil<HuayangNews> util = new ExcelUtil<HuayangNews>(HuayangNews.class);
return util.exportExcel(list, "新闻数据");
}
/**
* 获取新闻动态详情
*/
@PreAuthorize("@ss.hasPermi('huayang:news:query')")
@GetMapping(value = "/{newsId}")
public AjaxResult getInfo(@PathVariable("newsId") Long newsId)
{
return success(newsService.selectHuayangNewsById(newsId));
}
/**
* 新增新闻动态
*/
@PreAuthorize("@ss.hasPermi('huayang:news:add')")
@Log(title = "新闻动态", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody HuayangNews news)
{
news.setCreateBy(getUsername());
return toAjax(newsService.insertHuayangNews(news));
}
/**
* 修改新闻动态
*/
@PreAuthorize("@ss.hasPermi('huayang:news:edit')")
@Log(title = "新闻动态", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody HuayangNews news)
{
news.setUpdateBy(getUsername());
return toAjax(newsService.updateHuayangNews(news));
}
/**
* 删除新闻动态
*/
@PreAuthorize("@ss.hasPermi('huayang:news:remove')")
@Log(title = "新闻动态", businessType = BusinessType.DELETE)
@DeleteMapping("/{newsIds}")
public AjaxResult remove(@PathVariable Long[] newsIds)
{
return toAjax(newsService.deleteHuayangNewsByIds(newsIds));
}
}
@@ -1,220 +0,0 @@
package com.ruoyi.web.controller.huayang;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.HuayangFixedSection;
import com.ruoyi.system.domain.HuayangFixedSectionItem;
import com.ruoyi.system.service.IHuayangFixedSectionService;
/**
* 官网「新闻动态」页通栏横幅图(固定板块 news_banner,section_id=7)
*/
@RestController
@RequestMapping("/huayang/newsPage")
public class HuayangNewsPageBannerController extends BaseController
{
private static final Long NEWS_BANNER_SECTION_ID = 8L;
private static final String NEWS_BANNER_SECTION_CODE = "news_banner";
private static final String HOME_BANNER_SECTION_CODE = "section1";
private static final String NEWS_BANNER_ITEM_TYPE_HOME_NEWS_BG = "home_news_bg";
@Autowired
private IHuayangFixedSectionService huayangFixedSectionService;
@PreAuthorize("@ss.hasPermi('huayang:newsBanner:query')")
@GetMapping("/banner")
public AjaxResult getBanner()
{
HuayangFixedSection section = huayangFixedSectionService.selectHuayangFixedSectionBySectionId(NEWS_BANNER_SECTION_ID);
HuayangFixedSection full = huayangFixedSectionService.selectHuayangFixedSectionByCode(NEWS_BANNER_SECTION_CODE);
Map<String, Object> data = new HashMap<>(4);
data.put("backgroundImage", section != null ? section.getBackgroundImage() : null);
data.put("sectionText", section != null ? section.getSectionText() : null);
data.put("homeBackgroundImage", getHomeFirstBannerImage());
data.put("homeNewsBackgroundImage", getHomeNewsBackgroundImage(full));
return AjaxResult.success(data);
}
@PreAuthorize("@ss.hasPermi('huayang:newsBanner:edit')")
@Log(title = "新闻动态页顶横幅", businessType = BusinessType.UPDATE)
@PutMapping("/banner")
public AjaxResult saveBanner(@RequestBody NewsPageBannerSaveRequest body)
{
HuayangFixedSection existing = huayangFixedSectionService.selectHuayangFixedSectionBySectionId(NEWS_BANNER_SECTION_ID);
if (existing == null || !NEWS_BANNER_SECTION_CODE.equals(existing.getSectionCode()))
{
return AjaxResult.error("新闻动态顶图未初始化:请在数据库执行 backend/sql/huayang_news_dynamic_submenus.sql(含 fixed_section section_id=8)");
}
existing.setBackgroundImage(body != null ? body.getBackgroundImage() : null);
existing.setSectionText(body != null ? body.getSectionText() : null);
existing.setUpdateBy(getUsername());
int updated = huayangFixedSectionService.updateHuayangFixedSection(existing);
if (updated <= 0)
{
return toAjax(updated);
}
// 与首页顶部轮播图同步:保存新闻页横幅时,同时更新首页 section1 第一张轮播图
if (existing.getBackgroundImage() != null && !existing.getBackgroundImage().trim().isEmpty())
{
upsertHomeFirstBannerImage(existing.getBackgroundImage().trim());
}
if (body != null && body.getHomeNewsBackgroundImage() != null)
{
upsertHomeNewsBackgroundImage(body.getHomeNewsBackgroundImage());
}
return toAjax(updated);
}
private String getHomeFirstBannerImage()
{
HuayangFixedSection homeSection = huayangFixedSectionService.selectHuayangFixedSectionByCode(HOME_BANNER_SECTION_CODE);
if (homeSection == null || homeSection.getItemList() == null || homeSection.getItemList().isEmpty())
{
return null;
}
HuayangFixedSectionItem first = homeSection.getItemList().get(0);
return first != null ? first.getItemImage() : null;
}
private void upsertHomeFirstBannerImage(String image)
{
HuayangFixedSection homeSection = huayangFixedSectionService.selectHuayangFixedSectionByCode(HOME_BANNER_SECTION_CODE);
if (homeSection == null)
{
return;
}
List<HuayangFixedSectionItem> items = homeSection.getItemList();
HuayangFixedSectionItem first = (items != null && !items.isEmpty()) ? items.get(0) : null;
if (first != null && first.getItemId() != null)
{
HuayangFixedSectionItem update = new HuayangFixedSectionItem();
update.setItemId(first.getItemId());
update.setItemImage(image);
update.setUpdateBy(getUsername());
huayangFixedSectionService.updateHuayangFixedSectionItem(update);
return;
}
HuayangFixedSectionItem create = new HuayangFixedSectionItem();
create.setSectionId(homeSection.getSectionId());
create.setItemTitle("首页轮播图1");
create.setItemType("slide");
create.setSortOrder(1);
create.setStatus("0");
create.setItemImage(image);
create.setCreateBy(getUsername());
huayangFixedSectionService.insertHuayangFixedSectionItem(create);
}
private String getHomeNewsBackgroundImage(HuayangFixedSection newsBanner)
{
if (newsBanner == null || newsBanner.getItemList() == null || newsBanner.getItemList().isEmpty())
{
return null;
}
HuayangFixedSectionItem target = newsBanner.getItemList().stream()
.filter(it -> it != null && NEWS_BANNER_ITEM_TYPE_HOME_NEWS_BG.equals(it.getItemType()))
.findFirst()
.orElse(null);
return target != null ? target.getItemImage() : null;
}
private void upsertHomeNewsBackgroundImage(String image)
{
HuayangFixedSection banner = huayangFixedSectionService.selectHuayangFixedSectionByCode(NEWS_BANNER_SECTION_CODE);
if (banner == null || banner.getSectionId() == null)
{
return;
}
List<HuayangFixedSectionItem> items = banner.getItemList();
HuayangFixedSectionItem existing = null;
if (items != null)
{
for (HuayangFixedSectionItem it : items)
{
if (it != null && NEWS_BANNER_ITEM_TYPE_HOME_NEWS_BG.equals(it.getItemType()))
{
existing = it;
break;
}
}
}
if (existing != null && existing.getItemId() != null)
{
HuayangFixedSectionItem update = new HuayangFixedSectionItem();
update.setItemId(existing.getItemId());
update.setItemImage(image);
update.setUpdateBy(getUsername());
huayangFixedSectionService.updateHuayangFixedSectionItem(update);
return;
}
HuayangFixedSectionItem create = new HuayangFixedSectionItem();
create.setSectionId(banner.getSectionId());
create.setItemTitle("首页华阳新闻背景图");
create.setItemType(NEWS_BANNER_ITEM_TYPE_HOME_NEWS_BG);
create.setSortOrder(999);
create.setStatus("0");
create.setItemImage(image);
create.setCreateBy(getUsername());
huayangFixedSectionService.insertHuayangFixedSectionItem(create);
}
public static class NewsPageBannerSaveRequest
{
private String backgroundImage;
private String sectionText;
private String homeBackgroundImage;
private String homeNewsBackgroundImage;
public String getBackgroundImage()
{
return backgroundImage;
}
public void setBackgroundImage(String backgroundImage)
{
this.backgroundImage = backgroundImage;
}
public String getSectionText()
{
return sectionText;
}
public void setSectionText(String sectionText)
{
this.sectionText = sectionText;
}
public String getHomeBackgroundImage()
{
return homeBackgroundImage;
}
public void setHomeBackgroundImage(String homeBackgroundImage)
{
this.homeBackgroundImage = homeBackgroundImage;
}
public String getHomeNewsBackgroundImage()
{
return homeNewsBackgroundImage;
}
public void setHomeNewsBackgroundImage(String homeNewsBackgroundImage)
{
this.homeNewsBackgroundImage = homeNewsBackgroundImage;
}
}
}
@@ -0,0 +1,675 @@
package com.ruoyi.web.controller.huayang;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.HuayangI18n;
import com.ruoyi.system.domain.HuayangLanguage;
import com.ruoyi.system.service.IHuayangSiteConfigService;
import com.ruoyi.system.service.IHuayangI18nService;
import com.ruoyi.system.service.IHuayangLanguageService;
import com.ruoyi.system.service.ISysMenuService;
import com.ruoyi.system.service.translation.ITranslationScanner;
import com.ruoyi.system.service.translation.TranslationScannerRegistry;
import com.ruoyi.system.service.translation.vo.PageInfoVO;
import com.ruoyi.system.service.translation.vo.PageTranslationVO;
import com.ruoyi.system.service.translation.vo.TranslationSectionVO;
/**
* 翻译管理(按页面维度)
*
* <p>提供"翻译管理"菜单的核心能力:</p>
* <ul>
* <li>列出所有可翻译页面</li>
* <li>实时扫描某页面下所有可翻译条目(中文原文 + 已存在翻译)</li>
* <li>批量保存翻译</li>
* <li>全局完成度统计</li>
* </ul>
*
* @author huayang
*/
@RestController
@RequestMapping("/huayang/translation")
public class HuayangTranslationController extends BaseController
{
/** 默认语种(不参与翻译,只作为原文兜底) */
private static final String DEFAULT_LANG = "zh_cn";
/** 翻译菜单选择配置 key(保存 sys_menu.menu_id 列表,逗号分隔) */
private static final String MENU_SELECTION_CONFIG_KEY = "translation_menu_selected_ids";
@Autowired
private TranslationScannerRegistry scannerRegistry;
@Autowired
private IHuayangLanguageService languageService;
@Autowired
private IHuayangI18nService i18nService;
@Autowired
private ISysMenuService sysMenuService;
@Autowired
private IHuayangSiteConfigService siteConfigService;
/**
* 列出可翻译页面(用于左侧页面树)
*/
@PreAuthorize("@ss.hasPermi('huayang:translation:list')")
@GetMapping("/pages")
public AjaxResult listPages()
{
Set<String> selectedModules = resolveSelectedModules();
List<PageInfoVO> pages = new ArrayList<>();
for (PageInfoVO page : scannerRegistry.listAllPages())
{
if (selectedModules.isEmpty() || selectedModules.contains(page.getPageModule()))
{
pages.add(page);
}
}
return success(pages);
}
/**
* 列出当前启用的目标语种(不含默认语种 zh_cn)
*/
@PreAuthorize("@ss.hasPermi('huayang:translation:list')")
@GetMapping("/targetLangs")
public AjaxResult listTargetLangs()
{
List<String> langs = resolveTargetLangCodes();
List<HuayangLanguage> all = languageService.selectEnabledLanguageList();
// 返回完整对象(含 langName),便于前端展示
List<HuayangLanguage> result = new ArrayList<>();
if (all != null)
{
for (HuayangLanguage l : all)
{
if (l != null && langs.contains(l.getLangCode()))
{
result.add(l);
}
}
}
return success(result);
}
/**
* 扫描某页面,返回完整翻译条目(含中文原文与已存在翻译)
*/
@PreAuthorize("@ss.hasPermi('huayang:translation:list')")
@GetMapping("/scan")
public AjaxResult scan(@RequestParam("pageModule") String pageModule)
{
if (StringUtils.isBlank(pageModule))
{
return error("参数 pageModule 必填");
}
ITranslationScanner scanner = scannerRegistry.getScanner(pageModule);
if (scanner == null)
{
return error("未知页面模块:" + pageModule);
}
List<String> targetLangs = resolveTargetLangCodes();
if (targetLangs.isEmpty())
{
return error("未配置除默认语种外的可翻译语言,请先在「语言配置」中启用语种");
}
PageTranslationVO data = scanner.scan(targetLangs);
Set<String> allowedSections = resolveAllowedSections(pageModule);
if (!allowedSections.isEmpty())
{
List<TranslationSectionVO> sections = new ArrayList<>();
for (TranslationSectionVO section : data.getSections())
{
if (section != null && allowedSections.contains(section.getSectionKey()))
{
sections.add(section);
}
}
data.setSections(sections);
recalculatePageStats(data, targetLangs);
}
return success(data);
}
/**
* 获取菜单树选项(一级+二级)
*/
@PreAuthorize("@ss.hasPermi('huayang:translation:list')")
@GetMapping("/menuOptions")
public AjaxResult menuOptions()
{
List<SysMenu> rawMenus = sysMenuService.selectMenuList(new SysMenu(), getUserId());
List<SysMenu> candidateMenus = filterTranslationCandidateMenus(rawMenus);
List<SysMenu> tree = sysMenuService.buildMenuTree(candidateMenus);
List<MenuTreeNode> nodes = toMenuTreeNodes(tree);
return success(nodes);
}
/**
* 获取当前已选择的菜单 ID 列表
*/
@PreAuthorize("@ss.hasPermi('huayang:translation:list')")
@GetMapping("/menuSelection")
public AjaxResult menuSelection()
{
return success(new ArrayList<>(resolveSelectedMenuIds()));
}
/**
* 保存菜单选择
*/
@PreAuthorize("@ss.hasPermi('huayang:translation:edit')")
@Log(title = "翻译管理-菜单选择", businessType = BusinessType.UPDATE)
@PutMapping("/menuSelection")
public AjaxResult saveMenuSelection(@RequestBody MenuSelectionRequest request)
{
List<Long> menuIds = request == null ? Collections.emptyList() : request.getMenuIds();
String value = joinMenuIds(menuIds);
siteConfigService.upsertConfigValueByKey(
MENU_SELECTION_CONFIG_KEY,
value,
"翻译管理-菜单选择",
"翻译管理左侧菜单显示控制(sys_menu.menu_id 列表)"
);
return success();
}
/**
* 批量保存翻译条目
* <p>请求体示例:</p>
* <pre>
* {
* "pageModule": "home",
* "items": [
* { "langCode": "en_us", "key": "home.banner.1.title", "value": "Ocean Food" },
* { "langCode": "ja_jp", "key": "home.banner.1.title", "value": "" } // 空字符串=删除
* ]
* }
* </pre>
*/
@PreAuthorize("@ss.hasPermi('huayang:translation:edit')")
@Log(title = "翻译管理-批量保存", businessType = BusinessType.UPDATE)
@PutMapping("/batch")
public AjaxResult batchSave(@RequestBody BatchSaveRequest request)
{
if (request == null || request.getItems() == null || request.getItems().isEmpty())
{
return error("无可保存的条目");
}
if (StringUtils.isBlank(request.getPageModule()))
{
return error("参数 pageModule 必填");
}
List<String> validLangs = resolveTargetLangCodes();
List<HuayangI18n> rows = new ArrayList<>();
for (BatchSaveItem item : request.getItems())
{
if (item == null) continue;
if (StringUtils.isBlank(item.getLangCode()) || StringUtils.isBlank(item.getKey()))
{
continue;
}
// 安全校验:禁止写入未启用的语种或默认语种
if (DEFAULT_LANG.equals(item.getLangCode()))
{
continue;
}
if (!validLangs.contains(item.getLangCode()))
{
continue;
}
HuayangI18n row = new HuayangI18n();
row.setLangCode(item.getLangCode());
row.setI18nKey(item.getKey());
row.setI18nValue(item.getValue());
row.setPageModule(request.getPageModule());
rows.add(row);
}
if (rows.isEmpty())
{
return error("没有有效的条目可保存(语种未启用或缺失 key)");
}
IHuayangI18nService.BatchUpsertStat stat =
i18nService.batchUpsertTranslations(rows, getUsername());
Map<String, Object> data = new LinkedHashMap<>();
data.put("upserted", stat.getUpserted());
data.put("deleted", stat.getDeleted());
return AjaxResult.success("保存成功", data);
}
/**
* 全局完成度统计:每个页面 × 每个语种 → 已翻译数 / 总数
*/
@PreAuthorize("@ss.hasPermi('huayang:translation:list')")
@GetMapping("/stats")
public AjaxResult stats()
{
List<PageInfoVO> pages = scannerRegistry.listAllPages();
List<String> targetLangs = resolveTargetLangCodes();
List<Map<String, Object>> result = new ArrayList<>();
for (PageInfoVO page : pages)
{
ITranslationScanner scanner = scannerRegistry.getScanner(page.getPageModule());
if (scanner == null) continue;
// 实时扫描以获取总 key 数(数据量小,性能可接受;若数据量增长后可改为缓存)
PageTranslationVO pageVO = scanner.scan(targetLangs);
Map<String, Object> row = new LinkedHashMap<>();
row.put("pageModule", page.getPageModule());
row.put("pageName", page.getPageName());
row.put("totalKeys", pageVO.getTotalKeys());
row.put("translated", pageVO.getTranslatedByLang());
result.add(row);
}
Map<String, Object> data = new LinkedHashMap<>();
data.put("targetLangs", targetLangs);
data.put("pages", result);
return success(data);
}
/**
* 解析当前启用、且非默认语种的目标语种列表
*/
private List<String> resolveTargetLangCodes()
{
List<HuayangLanguage> langs = languageService.selectEnabledLanguageList();
if (langs == null) return Collections.emptyList();
List<String> result = new ArrayList<>();
for (HuayangLanguage l : langs)
{
if (l == null) continue;
String code = l.getLangCode();
if (StringUtils.isBlank(code) || DEFAULT_LANG.equals(code)) continue;
result.add(code);
}
return result;
}
private Set<Long> resolveSelectedMenuIds()
{
String value = siteConfigService.selectConfigValueByKey(MENU_SELECTION_CONFIG_KEY);
if (StringUtils.isBlank(value))
{
return Collections.emptySet();
}
Set<Long> result = new LinkedHashSet<>();
for (String part : value.split(","))
{
if (StringUtils.isBlank(part))
{
continue;
}
String trimmed = part.trim();
if (!trimmed.matches("\\d+"))
{
continue;
}
result.add(Long.valueOf(trimmed));
}
return result;
}
private Set<String> resolveSelectedModules()
{
Set<Long> selectedIds = resolveSelectedMenuIds();
if (selectedIds.isEmpty())
{
return Collections.emptySet();
}
Set<String> modules = new HashSet<>();
for (SysMenu menu : sysMenuService.selectMenuList(new SysMenu(), getUserId()))
{
if (menu == null || !selectedIds.contains(menu.getMenuId()))
{
continue;
}
String module = resolveMenuModule(menu);
if (StringUtils.isNotBlank(module))
{
modules.add(module);
}
}
return modules;
}
private Set<String> resolveAllowedSections(String pageModule)
{
Set<Long> selectedIds = resolveSelectedMenuIds();
if (selectedIds.isEmpty())
{
return Collections.emptySet();
}
Set<String> sections = new LinkedHashSet<>();
for (SysMenu menu : sysMenuService.selectMenuList(new SysMenu(), getUserId()))
{
if (menu == null || !selectedIds.contains(menu.getMenuId()))
{
continue;
}
String module = resolveMenuModule(menu);
if (module == null || !module.equals(pageModule))
{
continue;
}
String section = resolveSectionKey(pageModule, menu);
if (StringUtils.isNotBlank(section))
{
sections.add(section);
}
}
return sections;
}
private String resolveMenuModule(SysMenu menu)
{
String component = lower(menu.getComponent());
String path = lower(menu.getPath());
String perms = lower(menu.getPerms());
if (component.contains("homeworkbench")
|| component.contains("huayang/home")
|| component.contains("huayang/company")
|| component.contains("huayang/fixedsection")
|| path.contains("home")
|| perms.startsWith("huayang:home")
|| perms.startsWith("huayang:company")
|| perms.startsWith("huayang:fixedsection"))
{
return "home";
}
if (component.contains("huayang/product")
|| path.contains("product")
|| perms.startsWith("huayang:product")
|| perms.startsWith("huayang:productcategory")
|| perms.startsWith("huayang:productinquiry")
|| perms.startsWith("huayang:productbanner"))
{
return "product";
}
return null;
}
private String resolveSectionKey(String pageModule, SysMenu menu)
{
String perms = lower(menu.getPerms());
String path = lower(menu.getPath());
String component = lower(menu.getComponent());
if ("product".equals(pageModule))
{
if (perms.equals("huayang:productcategory:list") || path.equals("productcategory") || component.contains("huayang/productcategory/index"))
{
return "category";
}
if (perms.equals("huayang:product:list") || path.equals("product") || component.contains("huayang/product/index"))
{
return "item";
}
}
// home 目前不细分到 section,避免误过滤
return null;
}
private void recalculatePageStats(PageTranslationVO page, List<String> targetLangs)
{
int totalKeys = 0;
Map<String, Integer> translated = new LinkedHashMap<>();
for (String lang : targetLangs)
{
translated.put(lang, 0);
}
for (TranslationSectionVO section : page.getSections())
{
if (section == null || section.getItems() == null)
{
continue;
}
for (var item : section.getItems())
{
if (item == null || item.getFields() == null)
{
continue;
}
for (var field : item.getFields())
{
if (field == null)
{
continue;
}
totalKeys++;
for (String lang : targetLangs)
{
if (field.getTranslations() != null
&& field.getTranslations().get(lang) != null
&& StringUtils.isNotBlank(field.getTranslations().get(lang).getValue()))
{
translated.put(lang, translated.get(lang) + 1);
}
}
}
}
}
page.setTotalKeys(totalKeys);
page.setTranslatedByLang(translated);
}
private List<SysMenu> filterTranslationCandidateMenus(List<SysMenu> menus)
{
if (menus == null)
{
return Collections.emptyList();
}
Map<Long, SysMenu> byId = new HashMap<>();
for (SysMenu m : menus)
{
if (m != null && m.getMenuId() != null)
{
byId.put(m.getMenuId(), m);
}
}
Set<Long> includeIds = new HashSet<>();
for (SysMenu m : menus)
{
if (m == null || m.getMenuId() == null)
{
continue;
}
if (!isTranslationRelatedMenu(m))
{
continue;
}
includeIds.add(m.getMenuId());
Long parentId = m.getParentId();
while (parentId != null && parentId > 0 && byId.containsKey(parentId))
{
includeIds.add(parentId);
parentId = byId.get(parentId).getParentId();
}
}
List<SysMenu> result = new ArrayList<>();
for (SysMenu m : menus)
{
if (m != null && includeIds.contains(m.getMenuId()))
{
result.add(m);
}
}
result.sort(Comparator.comparing(SysMenu::getParentId, Comparator.nullsLast(Long::compareTo))
.thenComparing(SysMenu::getOrderNum, Comparator.nullsLast(Integer::compareTo))
.thenComparing(SysMenu::getMenuId, Comparator.nullsLast(Long::compareTo)));
return result;
}
private boolean isTranslationRelatedMenu(SysMenu menu)
{
if (menu == null)
{
return false;
}
if ("底部导航栏显示".equals(menu.getMenuName()))
{
return false;
}
String menuType = lower(menu.getMenuType());
if ("f".equals(menuType))
{
return false;
}
String perms = lower(menu.getPerms());
String component = lower(menu.getComponent());
String path = lower(menu.getPath());
return perms.startsWith("huayang:")
|| component.startsWith("huayang/")
|| path.startsWith("huayangsite/");
}
private List<MenuTreeNode> toMenuTreeNodes(List<SysMenu> menus)
{
List<MenuTreeNode> result = new ArrayList<>();
if (menus == null)
{
return result;
}
for (SysMenu m : menus)
{
if (m == null || m.getMenuId() == null)
{
continue;
}
MenuTreeNode node = new MenuTreeNode();
node.setId(m.getMenuId());
node.setLabel(m.getMenuName());
node.setPageModule(resolveMenuModule(m));
node.setSectionKey(resolveSectionKey(resolveMenuModule(m), m));
node.setPath(m.getPath());
node.setComponent(m.getComponent());
node.setPerms(m.getPerms());
node.setRemark(m.getRemark());
node.setChildren(toMenuTreeNodes(m.getChildren()));
result.add(node);
}
return result;
}
private String joinMenuIds(List<Long> menuIds)
{
if (menuIds == null || menuIds.isEmpty())
{
return "";
}
Set<Long> unique = new LinkedHashSet<>(menuIds);
StringBuilder sb = new StringBuilder();
for (Long id : unique)
{
if (id == null || id <= 0)
{
continue;
}
if (sb.length() > 0)
{
sb.append(",");
}
sb.append(id);
}
return sb.toString();
}
private String lower(String value)
{
return value == null ? "" : value.toLowerCase();
}
// ============== 请求体 DTO ==============
public static class MenuTreeNode
{
private Long id;
private String label;
private String pageModule;
private String sectionKey;
private String path;
private String component;
private String perms;
private String remark;
private List<MenuTreeNode> children = new ArrayList<>();
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getLabel() { return label; }
public void setLabel(String label) { this.label = label; }
public String getPageModule() { return pageModule; }
public void setPageModule(String pageModule) { this.pageModule = pageModule; }
public String getSectionKey() { return sectionKey; }
public void setSectionKey(String sectionKey) { this.sectionKey = sectionKey; }
public String getPath() { return path; }
public void setPath(String path) { this.path = path; }
public String getComponent() { return component; }
public void setComponent(String component) { this.component = component; }
public String getPerms() { return perms; }
public void setPerms(String perms) { this.perms = perms; }
public String getRemark() { return remark; }
public void setRemark(String remark) { this.remark = remark; }
public List<MenuTreeNode> getChildren() { return children; }
public void setChildren(List<MenuTreeNode> children) { this.children = children; }
}
public static class MenuSelectionRequest
{
private List<Long> menuIds = new ArrayList<>();
public List<Long> getMenuIds() { return menuIds; }
public void setMenuIds(List<Long> menuIds) { this.menuIds = menuIds; }
}
public static class BatchSaveRequest
{
private String pageModule;
private List<BatchSaveItem> items;
public String getPageModule() { return pageModule; }
public void setPageModule(String pageModule) { this.pageModule = pageModule; }
public List<BatchSaveItem> getItems() { return items; }
public void setItems(List<BatchSaveItem> items) { this.items = items; }
}
public static class BatchSaveItem
{
private String langCode;
private String key;
private String value;
public String getLangCode() { return langCode; }
public void setLangCode(String langCode) { this.langCode = langCode; }
public String getKey() { return key; }
public void setKey(String key) { this.key = key; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}
}
@@ -1,8 +1,12 @@
package com.ruoyi.web.controller.site;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -10,6 +14,8 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
@@ -48,10 +54,31 @@ public class SiteCompanyController extends BaseController
@Autowired
private IHuayangI18nService i18nService;
/** 占位串完整匹配表(逗号分隔初始化),调整时请改此类静态代码块 */
private static final int PLACEHOLDER_PLAIN_MAX_LEN = 200;
private static final int PLACEHOLDER_SINGLE_CHAR_REPEAT_MAX_LEN = 16;
private static final Set<String> PLACEHOLDER_EXACT_MATCH_SET;
static
{
Set<String> s = new HashSet<>();
for (String part : "qq,qw,qwe,qwer,qweqwe,qweqeqe,qweqweqweqwe,qqq,qqqq,ww,www,wwww,vv,vvv,vvvv,test,xxx,asd,asdf".split(","))
{
String t = part.trim().toLowerCase(Locale.ROOT);
if (!t.isEmpty())
{
s.add(t);
}
}
PLACEHOLDER_EXACT_MATCH_SET = Collections.unmodifiableSet(s);
}
/**
* 获取公司简介分块。
* <p>可选 {@code lang}:非 zh_cn 时,用 i18n key {@code company.section.{sectionCode}.title}、
* {@code company.section.{sectionCode}.content} 覆盖(content 受库表 i18n_value 长度限制,长文建议后续扩展字段)。</p>
* <p>可选 {@code lang}:非 zh_cn 时,优先 {@code extra_data.langContent}(翻译管理),缺省再用词典
* {@code company.section.{sectionCode}.title|content}。</p>
*/
@GetMapping("/sections")
public AjaxResult sections(@RequestParam(value = "lang", required = false) String lang)
@@ -63,11 +90,22 @@ public class SiteCompanyController extends BaseController
return success(list);
}
/** 与门户 {@code normalizeLangCode} 对齐:query 可为 ja-JP、JA_JP、jp 等 */
private static String normalizeSiteLang(String lang) {
if (StringUtils.isBlank(lang)) {
return "zh_cn";
}
return lang.trim().toLowerCase();
String s = lang.trim().toLowerCase(Locale.ROOT).replace('-', '_');
if (s.startsWith("zh")) {
return "zh_cn";
}
if (s.startsWith("en")) {
return "en_us";
}
if (s.startsWith("ja") || s.startsWith("jp")) {
return "ja_jp";
}
return s;
}
private void applySectionLocale(String langNorm, List<HuayangCompanySection> list) {
@@ -83,25 +121,207 @@ public class SiteCompanyController extends BaseController
keys.add("company.section." + s.getSectionCode() + ".content");
}
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
if (dict.isEmpty()) {
return;
}
for (HuayangCompanySection s : list) {
if (StringUtils.isBlank(s.getSectionCode())) {
continue;
}
String prefix = "company.section." + s.getSectionCode() + ".";
String t = dict.get(prefix + "title");
// 翻译管理写入的 extra_data.langContent 优先,但测试占位串不会下发到门户。
// 无有效手工译文时再尝试 huayang_i18n 词典。
String manualTitle = getManualLangTitleFromExtraData(s.getExtraData(), langNorm);
String dictTitle = dict.get(prefix + "title");
String t = pickBestLocalizedSectionText(manualTitle, dictTitle);
if (StringUtils.isNotEmpty(t)) {
s.setSectionTitle(t);
}
String c = dict.get(prefix + "content");
String manualContent = getManualLangContentFromExtraData(s.getExtraData(), langNorm);
String dictContent = dict.get(prefix + "content");
String c = pickBestLocalizedSectionText(manualContent, dictContent);
if (StringUtils.isNotEmpty(c)) {
s.setSectionContent(c);
}
}
}
/**
* 手工译文(翻译管理)优先;无手工时再尝试词典。
*/
private static String pickBestLocalizedSectionText(String manual, String dict) {
if (StringUtils.isNotBlank(manual) && !looksLikePlaceholderTranslation(manual)) {
return manual.trim();
}
if (StringUtils.isNotBlank(dict) && !looksLikePlaceholderTranslation(dict)) {
return dict;
}
return null;
}
/**
* 翻译后台误填的测试文案(完整匹配见 {@link #PLACEHOLDER_EXACT_MATCH_SET})。
*/
private static boolean looksLikePlaceholderTranslation(String raw) {
if (StringUtils.isBlank(raw)) {
return true;
}
String trimmed = raw.trim();
String plain =
trimmed.replaceAll("(?i)<br\\s*/?>", " ")
.replaceAll("<[^>]+>", " ")
.replaceAll("&nbsp;", " ")
.replaceAll("\\s+", " ")
.trim();
if (StringUtils.isBlank(plain)) {
return true;
}
if (plain.length() > PLACEHOLDER_PLAIN_MAX_LEN) {
return false;
}
String lower = plain.toLowerCase(Locale.ROOT);
if (PLACEHOLDER_EXACT_MATCH_SET.contains(lower)) {
return true;
}
if (lower.matches("^(qwe|wer|asd|zxc|www|vvv|xxx)+$")) {
return true;
}
if (plain.length() <= PLACEHOLDER_SINGLE_CHAR_REPEAT_MAX_LEN && plain.codePoints().distinct().count() == 1) {
return true;
}
return false;
}
/**
* 从 section.extra_data 解析手动翻译(标题)。
* <p>约定:{@code langContent.{lang}} 可为 {@code {"title":"...","content":"..."}},
* 标题从此处读取;旧版仅有正文 JSON 字符串时不返回标题。</p>
*/
private String getManualLangTitleFromExtraData(String extraData, String langNorm) {
Object entry = getLangContentEntry(extraData, langNorm);
if (!(entry instanceof JSONObject)) {
return null;
}
String title = ((JSONObject) entry).getString("title");
return StringUtils.isNotBlank(title) ? title : null;
}
/**
* 从 section.extra_data 解析手动翻译(正文)。
* <p>约定:
* <ul>
* <li>新结构:{@code langContent.en_us} = {@code {"title":"...","content":"..."}}</li>
* <li>旧结构:{@code langContent.en_us} = 整段 HTML 字符串(兼容)</li>
* </ul>
*/
private String getManualLangContentFromExtraData(String extraData, String langNorm) {
Object entry = getLangContentEntry(extraData, langNorm);
if (entry instanceof JSONObject) {
String content = ((JSONObject) entry).getString("content");
return StringUtils.isNotBlank(content) ? content : null;
}
if (entry instanceof String) {
String value = (String) entry;
return StringUtils.isNotBlank(value) ? value : null;
}
return null;
}
private Object getLangContentEntry(String extraData, String langNorm) {
if (StringUtils.isBlank(extraData) || StringUtils.isBlank(langNorm)) {
return null;
}
try {
JSONObject root = JSON.parseObject(extraData);
if (root == null) {
return null;
}
JSONObject langContent = root.getJSONObject("langContent");
if (langContent == null) {
langContent = root.getJSONObject("lang_content");
}
if (langContent == null) {
return null;
}
String canonical = normalizeSiteLang(langNorm);
Object direct = langContent.get(canonical);
if (direct != null) {
return direct;
}
for (String k : langContent.keySet()) {
if (canonical.equals(normalizeSiteLang(k))) {
return langContent.get(k);
}
}
return null;
} catch (Exception ex) {
return null;
}
}
private String storeExtraJsonField(String extraData, String langNorm, String field) {
Object entry = getLangContentEntry(extraData, langNorm);
if (!(entry instanceof JSONObject)) {
return null;
}
String v = ((JSONObject) entry).getString(field);
return StringUtils.isNotBlank(v) && !looksLikePlaceholderTranslation(v) ? v : null;
}
/**
* 店面展示页(about_showcase)条目的多语言:i18n 或 extra_data.langContent 的 name/address/phone。
*/
private void applyAboutShowcaseStoreLocale(String langNorm, List<HuayangCompanyStore> list) {
if ("zh_cn".equals(langNorm) || list == null || list.isEmpty()) {
return;
}
List<String> keys = new ArrayList<>();
for (HuayangCompanyStore s : list) {
if (s.getStoreId() == null) {
continue;
}
String sid = String.valueOf(s.getStoreId());
keys.add("company.store." + sid + ".name");
keys.add("company.store." + sid + ".address");
keys.add("company.store." + sid + ".phone");
}
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
for (HuayangCompanyStore s : list) {
if (s.getStoreId() == null) {
continue;
}
String sid = String.valueOf(s.getStoreId());
String p = "company.store." + sid + ".";
String name = dict.get(p + "name");
if (StringUtils.isBlank(name)) {
name = storeExtraJsonField(s.getExtraData(), langNorm, "name");
}
if (StringUtils.isBlank(name)) {
name = storeExtraJsonField(s.getExtraData(), langNorm, "title");
}
if (StringUtils.isNotEmpty(name)) {
s.setStoreName(name);
}
String address = dict.get(p + "address");
if (StringUtils.isBlank(address)) {
address = storeExtraJsonField(s.getExtraData(), langNorm, "address");
}
if (StringUtils.isNotEmpty(address)) {
s.setStoreAddress(address);
}
String phone = dict.get(p + "phone");
if (StringUtils.isBlank(phone)) {
phone = storeExtraJsonField(s.getExtraData(), langNorm, "phone");
}
if (StringUtils.isBlank(phone)) {
phone = storeExtraJsonField(s.getExtraData(), langNorm, "tel");
}
if (StringUtils.isNotEmpty(phone)) {
s.setStorePhone(phone);
}
}
}
/**
* 获取店面展示列表(首页等通用场景,不含页面专用/地图专表类型)
*/
@@ -117,29 +337,18 @@ public class SiteCompanyController extends BaseController
return success(list);
}
/**
* 门户「发展历程」页底部店面轮播:仅 {@code store_type = history}
*/
@GetMapping("/stores/history")
public AjaxResult storesForHistoryPage()
{
HuayangCompanyStore query = new HuayangCompanyStore();
query.setStatus("0");
query.setStoreType("history");
List<HuayangCompanyStore> list = storeService.selectHuayangCompanyStoreList(query);
return success(list);
}
/**
* 门户「店面展示页」(关于我们模块):仅 {@code store_type = about_showcase}
* <p>可选 {@code lang}:详见 {@link #applyAboutShowcaseStoreLocale}。</p>
*/
@GetMapping("/stores/about-showcase")
public AjaxResult storesForAboutPage()
public AjaxResult storesForAboutPage(@RequestParam(value = "lang", required = false) String lang)
{
HuayangCompanyStore query = new HuayangCompanyStore();
query.setStatus("0");
query.setStoreType("about_showcase");
List<HuayangCompanyStore> list = storeService.selectHuayangCompanyStoreList(query);
applyAboutShowcaseStoreLocale(normalizeSiteLang(lang), list);
return success(list);
}
@@ -147,20 +356,20 @@ public class SiteCompanyController extends BaseController
* 门户「全国门店」页左侧「店面位置」列表:仅 {@code store_type = map_location}
*/
@GetMapping("/stores/map-locations")
public AjaxResult storesForMapPage()
public AjaxResult storesForMapPage(@RequestParam(value = "lang", required = false) String lang)
{
HuayangCompanyStore query = new HuayangCompanyStore();
query.setStatus("0");
query.setStoreType("map_location");
List<HuayangCompanyStore> list = storeService.selectHuayangCompanyStoreList(query);
applyAboutShowcaseStoreLocale(normalizeSiteLang(lang), list);
return success(list);
}
/** 不参与通用 {@link #stores()} 列表的类型(专页/专块数据) */
private static boolean isExcludedFromGeneralStoresList(String storeType)
{
return "history".equals(storeType) || "about_showcase".equals(storeType)
|| "map_location".equals(storeType);
return "about_showcase".equals(storeType) || "map_location".equals(storeType);
}
/**
@@ -176,14 +385,53 @@ public class SiteCompanyController extends BaseController
}
/**
* 获取品质技术列表
* 获取品质技术列表。
* <p>可选 {@code lang}:非 zh_cn 时,优先使用 {@code extra_data.langContent}(翻译管理手工保存),
* 缺省再使用词典 {@code company.tech.{techId}.title|.content}。</p>
*/
@GetMapping("/tech")
public AjaxResult techList()
public AjaxResult techList(@RequestParam(value = "lang", required = false) String lang)
{
HuayangCompanyTech query = new HuayangCompanyTech();
query.setStatus("0");
List<HuayangCompanyTech> list = techService.selectHuayangCompanyTechList(query);
applyTechLocale(normalizeSiteLang(lang), list);
return success(list);
}
private void applyTechLocale(String langNorm, List<HuayangCompanyTech> list) {
if ("zh_cn".equals(langNorm) || list == null || list.isEmpty()) {
return;
}
List<String> keys = new ArrayList<>();
for (HuayangCompanyTech t : list) {
if (t.getTechId() == null) {
continue;
}
String id = String.valueOf(t.getTechId());
keys.add("company.tech." + id + ".title");
keys.add("company.tech." + id + ".content");
}
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
for (HuayangCompanyTech t : list) {
if (t.getTechId() == null) {
continue;
}
String id = String.valueOf(t.getTechId());
String prefix = "company.tech." + id + ".";
// 翻译管理写入的 extra_data.langContent 优先于词典:避免扫描任务把中文误写入 i18n 后盖住手工英/日文
String manualTitle = getManualLangTitleFromExtraData(t.getExtraData(), langNorm);
String dictTitle = dict.get(prefix + "title");
String title = pickBestLocalizedSectionText(manualTitle, dictTitle);
if (StringUtils.isNotEmpty(title)) {
t.setTechTitle(title);
}
String manualContent = getManualLangContentFromExtraData(t.getExtraData(), langNorm);
String dictContent = dict.get(prefix + "content");
String content = pickBestLocalizedSectionText(manualContent, dictContent);
if (StringUtils.isNotEmpty(content)) {
t.setTechContent(content);
}
}
}
}
@@ -1,6 +1,8 @@
package com.ruoyi.web.controller.site;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -13,7 +15,9 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.domain.HuayangCultureCategory;
import com.ruoyi.system.domain.HuayangCultureTag;
import com.ruoyi.system.service.IHuayangCultureCategoryService;
import com.ruoyi.system.service.IHuayangI18nService;
import com.ruoyi.system.service.IHuayangCultureTagService;
import org.apache.commons.lang3.StringUtils;
/**
* 门户:华阳文化页公开数据
@@ -30,17 +34,22 @@ public class SiteCultureController extends BaseController
@Autowired
private IHuayangCultureTagService cultureTagService;
@Autowired
private IHuayangI18nService i18nService;
@Anonymous
@GetMapping("/categories/public")
public AjaxResult categoriesPublic()
public AjaxResult categoriesPublic(@RequestParam(value = "lang", required = false) String lang)
{
List<HuayangCultureCategory> list = cultureCategoryService.selectHuayangCultureCategoryPublicList();
applyCultureCategoryLocale(normalizeSiteLang(lang), list);
return success(list);
}
@Anonymous
@GetMapping("/tags/public")
public AjaxResult tagsPublic(@RequestParam("categoryId") Long categoryId)
public AjaxResult tagsPublic(@RequestParam("categoryId") Long categoryId,
@RequestParam(value = "lang", required = false) String lang)
{
if (categoryId == null || categoryId <= 0)
{
@@ -52,12 +61,14 @@ public class SiteCultureController extends BaseController
return error("分类不存在或已停用");
}
List<HuayangCultureTag> list = cultureTagService.selectHuayangCultureTagPublicListByCategoryId(categoryId);
applyCultureTagLocale(normalizeSiteLang(lang), list);
return success(list);
}
@Anonymous
@GetMapping("/tags/{tagId}/public")
public AjaxResult tagDetailPublic(@PathVariable("tagId") Long tagId)
public AjaxResult tagDetailPublic(@PathVariable("tagId") Long tagId,
@RequestParam(value = "lang", required = false) String lang)
{
HuayangCultureTag tag = cultureTagService.selectHuayangCultureTagPublicById(tagId);
if (tag == null)
@@ -70,6 +81,64 @@ public class SiteCultureController extends BaseController
return error("内容不存在或已下架");
}
cultureTagService.incrementReadCount(tagId);
return success(cultureTagService.selectHuayangCultureTagById(tagId));
HuayangCultureTag detail = cultureTagService.selectHuayangCultureTagById(tagId);
applyCultureTagDetailLocale(normalizeSiteLang(lang), detail);
return success(detail);
}
private static String normalizeSiteLang(String lang) {
if (StringUtils.isBlank(lang)) {
return "zh_cn";
}
return lang.trim().toLowerCase();
}
private void applyCultureCategoryLocale(String langNorm, List<HuayangCultureCategory> list) {
if ("zh_cn".equals(langNorm) || list == null || list.isEmpty()) return;
List<String> keys = new ArrayList<>();
for (HuayangCultureCategory item : list) {
if (item.getCategoryId() != null) {
keys.add("culture.category." + item.getCategoryId() + ".name");
}
}
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
for (HuayangCultureCategory item : list) {
if (item.getCategoryId() == null) continue;
String v = dict.get("culture.category." + item.getCategoryId() + ".name");
if (StringUtils.isNotEmpty(v)) item.setCategoryName(v);
}
}
private void applyCultureTagLocale(String langNorm, List<HuayangCultureTag> list) {
if ("zh_cn".equals(langNorm) || list == null || list.isEmpty()) return;
List<String> keys = new ArrayList<>();
for (HuayangCultureTag item : list) {
if (item.getTagId() != null) {
keys.add("culture.tag." + item.getTagId() + ".title");
keys.add("culture.tag." + item.getTagId() + ".content");
}
}
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
for (HuayangCultureTag item : list) {
if (item.getTagId() == null) continue;
String prefix = "culture.tag." + item.getTagId() + ".";
String t = dict.get(prefix + "title");
if (StringUtils.isNotEmpty(t)) item.setTagTitle(t);
String c = dict.get(prefix + "content");
if (StringUtils.isNotEmpty(c)) item.setTagContent(c);
}
}
private void applyCultureTagDetailLocale(String langNorm, HuayangCultureTag detail) {
if ("zh_cn".equals(langNorm) || detail == null || detail.getTagId() == null) return;
String prefix = "culture.tag." + detail.getTagId() + ".";
List<String> keys = new ArrayList<>(2);
keys.add(prefix + "title");
keys.add(prefix + "content");
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
String t = dict.get(prefix + "title");
if (StringUtils.isNotEmpty(t)) detail.setTagTitle(t);
String c = dict.get(prefix + "content");
if (StringUtils.isNotEmpty(c)) detail.setTagContent(c);
}
}
@@ -3,6 +3,7 @@ package com.ruoyi.web.controller.site;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Anonymous;
@@ -10,10 +11,9 @@ import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.HuayangContact;
import com.ruoyi.system.domain.HuayangJoinApply;
import com.ruoyi.system.domain.HuayangMessage;
import com.ruoyi.system.service.IHuayangContactService;
import com.ruoyi.system.service.IHuayangJoinApplyService;
import com.ruoyi.system.service.IHuayangI18nService;
import com.ruoyi.system.service.IHuayangMessageService;
import com.ruoyi.system.service.IHuayangSiteConfigService;
@@ -33,34 +33,69 @@ public class SiteCustomerController extends BaseController
private IHuayangMessageService messageService;
@Autowired
private IHuayangJoinApplyService joinApplyService;
private IHuayangSiteConfigService siteConfigService;
@Autowired
private IHuayangSiteConfigService siteConfigService;
private IHuayangI18nService i18nService;
/**
* 获取联系信息(公开)
*/
@Anonymous
@GetMapping("/contact/list")
public AjaxResult contactList()
public AjaxResult contactList(@RequestParam(value = "lang", required = false) String lang)
{
List<HuayangContact> list = contactService.selectEnabledContactList();
Map<String, String> siteValueByType = new HashMap<>(3);
siteValueByType.put("phone", siteConfigService.selectConfigValueByKey("contact_phone"));
siteValueByType.put("email", siteConfigService.selectConfigValueByKey("contact_email"));
siteValueByType.put("address", siteConfigService.selectConfigValueByKey("contact_address"));
for (HuayangContact item : list)
if (list == null || list.isEmpty())
{
String siteValue = siteValueByType.get(item.getContactType());
if (StringUtils.isNotBlank(siteValue))
list = buildFallbackContacts();
}
else
{
Map<String, String> siteValueByType = new HashMap<>(3);
siteValueByType.put("phone", siteConfigService.selectConfigValueByKey("contact_phone"));
siteValueByType.put("email", siteConfigService.selectConfigValueByKey("contact_email"));
siteValueByType.put("address", siteConfigService.selectConfigValueByKey("contact_address"));
for (HuayangContact item : list)
{
item.setContactValue(siteValue);
if (item == null || StringUtils.isNotBlank(item.getContactValue()))
{
continue;
}
String siteValue = siteValueByType.get(item.getContactType());
if (StringUtils.isNotBlank(siteValue))
{
item.setContactValue(siteValue);
}
}
}
applyContactLocale(normalizeSiteLang(lang), list);
return success(list);
}
private List<HuayangContact> buildFallbackContacts()
{
List<HuayangContact> list = new ArrayList<>();
appendConfigGroup(list, siteConfigService.selectConfigValueByKey("contact_phone"), "phone", "商务热线", 0);
appendConfigGroup(list, siteConfigService.selectConfigValueByKey("contact_email"), "email", "E-mail", 1);
appendConfigGroup(list, siteConfigService.selectConfigValueByKey("contact_address"), "address", "地址", 2);
return list;
}
private void appendConfigGroup(List<HuayangContact> list, String raw, String type, String defaultLabel, int sortOrder)
{
if (StringUtils.isBlank(raw))
{
return;
}
HuayangContact item = new HuayangContact();
item.setContactType(type);
item.setContactLabel(defaultLabel);
item.setContactValue(raw.trim());
item.setSortOrder(sortOrder);
list.add(item);
}
/**
* 提交在线留言(公开)
*/
@@ -78,20 +113,31 @@ public class SiteCustomerController extends BaseController
return error("提交失败,请稍后重试");
}
/**
* 提交招商申请(公开)
*/
@Anonymous
@PostMapping("/contact/join")
public AjaxResult submitJoinApply(@RequestBody HuayangJoinApply apply)
{
apply.setCreateBy("anonymous");
apply.setApplyStatus("0");
int rows = joinApplyService.insertHuayangJoinApply(apply);
if (rows > 0)
{
return success("申请提交成功,我们的招商经理会尽快与您联系!");
private static String normalizeSiteLang(String lang) {
if (StringUtils.isBlank(lang)) {
return "zh_cn";
}
return lang.trim().toLowerCase();
}
private void applyContactLocale(String langNorm, List<HuayangContact> list) {
if ("zh_cn".equals(langNorm) || list == null || list.isEmpty()) {
return;
}
List<String> keys = new ArrayList<>(3);
keys.add("contact.phone.label");
keys.add("contact.email.label");
keys.add("contact.address.label");
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
for (HuayangContact item : list) {
if (item == null || StringUtils.isBlank(item.getContactType())) {
continue;
}
String labelKey = "contact." + item.getContactType() + ".label";
String translatedLabel = dict.get(labelKey);
if (StringUtils.isNotBlank(translatedLabel)) {
item.setContactLabel(translatedLabel);
}
}
return error("提交失败,请稍后重试");
}
}
@@ -1,14 +1,26 @@
package com.ruoyi.web.controller.site;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.service.IHuayangFixedSectionService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.domain.HuayangFixedSection;
import com.ruoyi.system.domain.HuayangFixedSectionItem;
import com.ruoyi.system.domain.HuayangI18n;
import com.ruoyi.system.mapper.HuayangI18nMapper;
import com.ruoyi.system.service.IHuayangFixedSectionService;
import com.ruoyi.system.service.IHuayangI18nService;
/**
* 门户固定板块公开接口
@@ -18,15 +30,166 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/site/fixedSection")
public class SiteFixedSectionController extends BaseController
{
private static final String DEFAULT_LANG = "zh_cn";
@Autowired
private IHuayangFixedSectionService fixedSectionService;
@Autowired
private HuayangI18nMapper i18nMapper;
@Autowired
private IHuayangI18nService i18nService;
/**
* 按编码获取固定板块(包含启用子项)
*/
@GetMapping("/code/{sectionCode}")
public AjaxResult getByCode(@PathVariable("sectionCode") String sectionCode)
public AjaxResult getByCode(@PathVariable("sectionCode") String sectionCode,
@RequestParam(value = "lang", required = false) String lang)
{
return success(fixedSectionService.selectHuayangFixedSectionByCode(sectionCode));
HuayangFixedSection section = fixedSectionService.selectHuayangFixedSectionByCode(sectionCode);
applyTranslations(section, normalizeLang(lang));
return success(section);
}
private void applyTranslations(HuayangFixedSection section, String lang)
{
if (section == null || DEFAULT_LANG.equals(lang))
{
return;
}
String code = section.getSectionCode();
if (StringUtils.isBlank(code))
{
return;
}
List<String> keys = new ArrayList<>();
keys.add("home." + code + ".main.title");
keys.add("home." + code + ".main.text");
if (section.getItemList() != null)
{
for (HuayangFixedSectionItem item : section.getItemList())
{
if (item == null || item.getItemId() == null)
{
continue;
}
keys.add("home." + code + ".item." + item.getItemId() + ".title");
keys.add("home." + code + ".item." + item.getItemId() + ".desc");
}
}
Map<String, String> translations = selectRawTranslations(lang, keys);
if (translations == null || translations.isEmpty())
{
return;
}
String mainTitle = translations.get("home." + code + ".main.title");
if (StringUtils.isNotBlank(mainTitle))
{
section.setSectionName(mainTitle);
}
String mainText = translations.get("home." + code + ".main.text");
if (StringUtils.isNotBlank(mainText))
{
section.setSectionText(mainText);
}
if (section.getItemList() != null)
{
for (HuayangFixedSectionItem item : section.getItemList())
{
if (item == null || item.getItemId() == null)
{
continue;
}
String prefix = "home." + code + ".item." + item.getItemId();
String title = translations.get(prefix + ".title");
if (StringUtils.isNotBlank(title))
{
item.setItemTitle(title);
}
String desc = translations.get(prefix + ".desc");
if (StringUtils.isNotBlank(desc))
{
item.setItemDesc(desc);
}
}
}
}
private Map<String, String> selectRawTranslations(String lang, List<String> keys)
{
Map<String, String> translations = new HashMap<>();
if (StringUtils.isBlank(lang) || keys == null || keys.isEmpty())
{
return translations;
}
try
{
List<HuayangI18n> rows = i18nMapper.selectHuayangI18nByLangAndKeyList(lang, keys);
if (rows != null)
{
for (HuayangI18n row : rows)
{
if (row != null && StringUtils.isNotBlank(row.getI18nKey()) && row.getI18nValue() != null)
{
translations.put(row.getI18nKey(), row.getI18nValue());
}
}
}
}
catch (RuntimeException | LinkageError ignored)
{
List<Map<String, String>> rows = i18nService.selectTranslationByLang(lang);
if (rows == null)
{
return translations;
}
for (Map<String, String> row : rows)
{
String key = row.get("i18nKey");
if (StringUtils.isBlank(key))
{
key = row.get("i18n_key");
}
if (!keys.contains(key))
{
continue;
}
String value = row.get("i18nValue");
if (value == null)
{
value = row.get("i18n_value");
}
if (value != null)
{
translations.put(key, value);
}
}
}
return translations;
}
private String normalizeLang(String raw)
{
String value = StringUtils.defaultString(raw).trim().toLowerCase(Locale.ROOT).replace('-', '_');
if (StringUtils.isBlank(value) || value.startsWith("zh"))
{
return DEFAULT_LANG;
}
if (value.startsWith("en"))
{
return "en_us";
}
if (value.startsWith("ja") || value.startsWith("jp"))
{
return "ja_jp";
}
return value;
}
}
@@ -6,12 +6,10 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.domain.HuayangHomeBanner;
import com.ruoyi.system.domain.HuayangHomeFeature;
import com.ruoyi.system.domain.HuayangProduct;
import com.ruoyi.system.domain.HuayangNews;
import com.ruoyi.system.service.IHuayangHomeBannerService;
import com.ruoyi.system.service.IHuayangHomeFeatureService;
import com.ruoyi.system.service.IHuayangI18nService;
import com.ruoyi.system.service.IHuayangProductService;
import com.ruoyi.system.service.IHuayangNewsService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@@ -34,8 +32,6 @@ public class SiteHomeController extends BaseController {
@Autowired
private IHuayangProductService productService;
@Autowired
private IHuayangNewsService newsService;
@Autowired
private IHuayangI18nService i18nService;
/**
@@ -45,7 +41,6 @@ public class SiteHomeController extends BaseController {
* <li>轮播标题:{@code home.banner.{bannerId}.title}</li>
* <li>特色标题/描述:{@code home.feature.{featureId}.title}、{@code home.feature.{featureId}.desc}</li>
* <li>推荐产品名称:{@code home.product.{productId}.name}</li>
* <li>推荐新闻标题:{@code home.news.{newsId}.title}</li>
* </ul>
*/
@Anonymous
@@ -59,25 +54,17 @@ public class SiteHomeController extends BaseController {
queryFeature.setStatus("0");
List<HuayangHomeFeature> features = featureService.selectHuayangHomeFeatureList(queryFeature);
// 首页推荐产品
HuayangProduct queryProduct = new HuayangProduct();
queryProduct.setStatus("0");
queryProduct.setIsHome("1"); // 推荐到首页
queryProduct.setIsHome("1");
List<HuayangProduct> products = productService.selectHuayangProductList(queryProduct);
// 首页推荐新闻
HuayangNews queryNews = new HuayangNews();
queryNews.setStatus("0");
queryNews.setIsHome("1"); // 推荐到首页
List<HuayangNews> news = newsService.selectHuayangNewsList(queryNews);
applyHomePublicLocale(normalizeSiteLang(lang), banners, features, products, news);
applyHomePublicLocale(normalizeSiteLang(lang), banners, features, products);
Map<String, Object> data = new HashMap<>();
data.put("banners", banners);
data.put("features", features);
data.put("products", products);
data.put("news", news);
return success(data);
}
@@ -88,11 +75,8 @@ public class SiteHomeController extends BaseController {
return lang.trim().toLowerCase();
}
/**
* 中文库表为基准;其它语言在 huayang_i18n 中维护对应 key 后在此覆盖。
*/
private void applyHomePublicLocale(String langNorm, List<HuayangHomeBanner> banners,
List<HuayangHomeFeature> features, List<HuayangProduct> products, List<HuayangNews> news) {
List<HuayangHomeFeature> features, List<HuayangProduct> products) {
if ("zh_cn".equals(langNorm)) {
return;
}
@@ -119,13 +103,6 @@ public class SiteHomeController extends BaseController {
}
}
}
if (news != null) {
for (HuayangNews n : news) {
if (n.getNewsId() != null) {
keys.add("home.news." + n.getNewsId() + ".title");
}
}
}
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
if (dict.isEmpty()) {
return;
@@ -167,16 +144,5 @@ public class SiteHomeController extends BaseController {
}
}
}
if (news != null) {
for (HuayangNews n : news) {
if (n.getNewsId() == null) {
continue;
}
String v = dict.get("home.news." + n.getNewsId() + ".title");
if (StringUtils.isNotEmpty(v)) {
n.setNewsTitle(v);
}
}
}
}
}
@@ -1,8 +1,12 @@
package com.ruoyi.web.controller.site;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Anonymous;
@@ -10,6 +14,7 @@ import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.domain.HuayangCompanyHonor;
import com.ruoyi.system.service.IHuayangCompanyHonorService;
import com.ruoyi.system.service.IHuayangI18nService;
/**
* 门户站点公开接口:资质荣誉
@@ -23,14 +28,69 @@ public class SiteHonorController extends BaseController
@Autowired
private IHuayangCompanyHonorService huayangCompanyHonorService;
@Autowired
private IHuayangI18nService i18nService;
/**
* 对外公开:正常状态的资质荣誉列表(无需登录)
*/
@Anonymous
@GetMapping("/public")
public AjaxResult publicList()
public AjaxResult publicList(@RequestParam(value = "lang", required = false) String lang)
{
List<HuayangCompanyHonor> list = huayangCompanyHonorService.selectHuayangCompanyHonorPublicList();
applyHonorLocale(normalizeSiteLang(lang), list);
return success(list);
}
private static String normalizeSiteLang(String lang)
{
if (StringUtils.isBlank(lang))
{
return "zh_cn";
}
return lang.trim().toLowerCase();
}
private void applyHonorLocale(String langNorm, List<HuayangCompanyHonor> list)
{
if ("zh_cn".equals(langNorm) || list == null || list.isEmpty())
{
return;
}
List<String> keys = new ArrayList<>();
for (HuayangCompanyHonor item : list)
{
if (item == null || item.getHonorId() == null)
{
continue;
}
keys.add("company.honor." + item.getHonorId() + ".title");
keys.add("company.honor." + item.getHonorId() + ".desc");
}
if (keys.isEmpty())
{
return;
}
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
for (HuayangCompanyHonor item : list)
{
if (item == null || item.getHonorId() == null)
{
continue;
}
String titleKey = "company.honor." + item.getHonorId() + ".title";
String descKey = "company.honor." + item.getHonorId() + ".desc";
String title = dict.get(titleKey);
String desc = dict.get(descKey);
if (StringUtils.isNotBlank(title))
{
item.setHonorTitle(title);
}
if (StringUtils.isNotBlank(desc))
{
item.setHonorDesc(desc);
}
}
}
}
@@ -1,148 +0,0 @@
package com.ruoyi.web.controller.site;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.system.domain.HuayangNews;
import com.ruoyi.system.service.IHuayangNewsService;
import com.ruoyi.system.service.ISysDictDataService;
/**
* 门户站点公开接口:新闻动态
*
* @author huayang
*/
@RestController
@RequestMapping("/site/news")
public class SiteNewsController extends BaseController
{
private static final String NEWS_TYPE_DICT = "huayang_news_type";
@Autowired
private IHuayangNewsService newsService;
@Autowired
private ISysDictDataService dictDataService;
/**
* 对外公开:新闻列表(支持类型筛选、分页)
*/
@Anonymous
@GetMapping("/public")
public AjaxResult publicList(
@RequestParam(required = false) String newsType,
@RequestParam(required = false) Integer pageNum,
@RequestParam(required = false) Integer pageSize)
{
// 默认返回10条
if (pageSize == null || pageSize <= 0) {
pageSize = 10;
}
if (pageNum == null || pageNum <= 0) {
pageNum = 1;
}
int offset = (pageNum - 1) * pageSize;
long total = newsService.countNewsPublic(newsType);
List<HuayangNews> list = newsService.selectNewsForHome(newsType, offset, pageSize);
Map<String, Object> data = new HashMap<>(4);
data.put("rows", list);
data.put("total", total);
return success(data);
}
/**
* 对外公开:新闻详情
*/
@Anonymous
@GetMapping("/{newsId}/public")
public AjaxResult publicDetail(@PathVariable("newsId") Long newsId)
{
HuayangNews news = newsService.selectHuayangNewsById(newsId);
if (news == null || !"0".equals(news.getStatus()))
{
return error("新闻不存在或已下架");
}
// 增加阅读次数
newsService.incrementReadCount(newsId);
return success(news);
}
/**
* 对外公开:首页推荐新闻(与 /site/home/public 中 news 列表一致,供首页聚合接口失败时单独拉取)
*/
@Anonymous
@GetMapping("/homeRecommend")
public AjaxResult homeRecommend(@RequestParam(required = false) Integer limit)
{
if (limit == null || limit <= 0)
{
limit = 3;
}
if (limit > 20)
{
limit = 20;
}
HuayangNews query = new HuayangNews();
query.setStatus("0");
query.setIsHome("1");
List<HuayangNews> list = newsService.selectHuayangNewsList(query);
if (list == null)
{
list = List.of();
}
else if (list.size() > limit)
{
list = new ArrayList<>(list.subList(0, limit));
}
return success(list);
}
/**
* 对外公开:获取最新新闻(用于首页展示)
*/
@Anonymous
@GetMapping("/latest")
public AjaxResult latestNews(
@RequestParam(required = false) String newsType,
@RequestParam(required = false) Integer limit)
{
if (limit == null || limit <= 0) {
limit = 5;
}
List<HuayangNews> list = newsService.selectNewsForHome(newsType, null, limit);
return success(list);
}
/**
* 对外公开:新闻类型(来源 sys_dict_data,dict_type=huayang_news_type)
*/
@Anonymous
@GetMapping("/types")
public AjaxResult types()
{
SysDictData query = new SysDictData();
query.setDictType(NEWS_TYPE_DICT);
query.setStatus("0");
List<SysDictData> list = dictDataService.selectDictDataList(query);
List<Map<String, Object>> data = list.stream().map(item -> {
Map<String, Object> row = new HashMap<>(4);
row.put("label", item.getDictLabel());
row.put("value", item.getDictValue());
row.put("sort", item.getDictSort());
return row;
}).collect(Collectors.toList());
return success(data);
}
}
@@ -1,6 +1,8 @@
package com.ruoyi.web.controller.site;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -17,7 +19,9 @@ import com.ruoyi.system.domain.HuayangProductCategory;
import com.ruoyi.system.domain.HuayangProductInquiry;
import com.ruoyi.system.service.IHuayangProductCategoryService;
import com.ruoyi.system.service.IHuayangProductInquiryService;
import com.ruoyi.system.service.IHuayangI18nService;
import com.ruoyi.system.service.IHuayangProductService;
import org.apache.commons.lang3.StringUtils;
/**
* 门户站点公开接口:产品中心
@@ -37,14 +41,18 @@ public class SiteProductController extends BaseController
@Autowired
private IHuayangProductInquiryService productInquiryService;
@Autowired
private IHuayangI18nService i18nService;
/**
* 对外公开:产品分类列表(无需登录)
*/
@Anonymous
@GetMapping("/category/public")
public AjaxResult publicCategoryList()
public AjaxResult publicCategoryList(@RequestParam(required = false) String lang)
{
List<HuayangProductCategory> list = productCategoryService.selectHuayangProductCategoryPublicList();
applyProductCategoryLocale(normalizeSiteLang(lang), list);
return success(list);
}
@@ -55,9 +63,11 @@ public class SiteProductController extends BaseController
@GetMapping("/public")
public AjaxResult publicList(
@RequestParam(required = false) Long categoryId,
@RequestParam(required = false) String keyword)
@RequestParam(required = false) String keyword,
@RequestParam(required = false) String lang)
{
List<HuayangProduct> list = productService.selectHuayangProductPublicList(categoryId, keyword);
applyProductLocale(normalizeSiteLang(lang), list);
return success(list);
}
@@ -66,13 +76,15 @@ public class SiteProductController extends BaseController
*/
@Anonymous
@GetMapping("/{productId}/public")
public AjaxResult publicDetail(@PathVariable("productId") Long productId)
public AjaxResult publicDetail(@PathVariable("productId") Long productId,
@RequestParam(required = false) String lang)
{
HuayangProduct product = productService.selectHuayangProductById(productId);
if (product == null || !"0".equals(product.getStatus()))
{
return error("产品不存在或已下架");
}
applyProductDetailLocale(normalizeSiteLang(lang), product);
return success(product);
}
@@ -93,4 +105,62 @@ public class SiteProductController extends BaseController
}
return error("提交失败,请稍后重试");
}
private static String normalizeSiteLang(String lang) {
if (StringUtils.isBlank(lang)) {
return "zh_cn";
}
return lang.trim().toLowerCase();
}
private void applyProductCategoryLocale(String langNorm, List<HuayangProductCategory> list) {
if ("zh_cn".equals(langNorm) || list == null || list.isEmpty()) return;
List<String> keys = new ArrayList<>();
for (HuayangProductCategory item : list) {
if (item.getCategoryId() != null) {
keys.add("product.category." + item.getCategoryId() + ".name");
}
}
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
for (HuayangProductCategory item : list) {
if (item.getCategoryId() == null) continue;
String v = dict.get("product.category." + item.getCategoryId() + ".name");
if (StringUtils.isNotEmpty(v)) {
item.setCategoryName(v);
}
}
}
private void applyProductLocale(String langNorm, List<HuayangProduct> list) {
if ("zh_cn".equals(langNorm) || list == null || list.isEmpty()) return;
List<String> keys = new ArrayList<>();
for (HuayangProduct item : list) {
if (item.getProductId() != null) {
keys.add("product.item." + item.getProductId() + ".name");
keys.add("product.item." + item.getProductId() + ".desc");
}
}
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
for (HuayangProduct item : list) {
if (item.getProductId() == null) continue;
String prefix = "product.item." + item.getProductId() + ".";
String n = dict.get(prefix + "name");
if (StringUtils.isNotEmpty(n)) item.setProductName(n);
String d = dict.get(prefix + "desc");
if (StringUtils.isNotEmpty(d)) item.setProductDesc(d);
}
}
private void applyProductDetailLocale(String langNorm, HuayangProduct product) {
if ("zh_cn".equals(langNorm) || product == null || product.getProductId() == null) return;
List<String> keys = new ArrayList<>();
String prefix = "product.item." + product.getProductId() + ".";
keys.add(prefix + "name");
keys.add(prefix + "desc");
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
String n = dict.get(prefix + "name");
if (StringUtils.isNotEmpty(n)) product.setProductName(n);
String d = dict.get(prefix + "desc");
if (StringUtils.isNotEmpty(d)) product.setProductDesc(d);
}
}
@@ -12,9 +12,7 @@ import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.domain.HuayangProduct;
import com.ruoyi.system.domain.HuayangNews;
import com.ruoyi.system.service.IHuayangProductService;
import com.ruoyi.system.service.IHuayangNewsService;
/**
* 全站搜索接口
@@ -29,11 +27,8 @@ public class SiteSearchController extends BaseController
@Autowired
private IHuayangProductService productService;
@Autowired
private IHuayangNewsService newsService;
/**
* 根据关键词搜索产品和新闻
* 根据关键词搜索产品
*/
@GetMapping("/all")
public AjaxResult search(@RequestParam(value = "keyword", required = false) String keyword)
@@ -41,17 +36,10 @@ public class SiteSearchController extends BaseController
Map<String, Object> results = new HashMap<>();
if (keyword != null && !keyword.trim().isEmpty()) {
// 搜索产品
HuayangProduct productQuery = new HuayangProduct();
productQuery.setProductName(keyword);
List<HuayangProduct> products = productService.selectHuayangProductList(productQuery);
results.put("products", products);
// 搜索新闻
HuayangNews newsQuery = new HuayangNews();
newsQuery.setNewsTitle(keyword);
List<HuayangNews> news = newsService.selectHuayangNewsList(newsQuery);
results.put("news", news);
}
return success(results);
@@ -1,15 +1,22 @@
package com.ruoyi.web.controller.site;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.domain.HuayangCompanyTimeline;
import com.ruoyi.system.service.IHuayangCompanyTimelineService;
import com.ruoyi.system.service.IHuayangI18nService;
/**
* 门户站点公开接口:发展历程
@@ -23,14 +30,131 @@ public class SiteTimelineController extends BaseController
@Autowired
private IHuayangCompanyTimelineService huayangCompanyTimelineService;
@Autowired
private IHuayangI18nService i18nService;
/**
* 对外公开:正常状态的发展历程列表(无需登录)
* 对外公开:正常状态的发展历程列表(无需登录)。
* <p>可选 {@code lang}:非 zh_cn 时用 i18n key {@code company.timeline.{timelineId}.title|summary|detail},
* 暂无则读 {@code extra_data.langContent}(对象字段 title、summary、detail;旧版整段字符串视为 detail)。</p>
*/
@Anonymous
@GetMapping("/public")
public AjaxResult publicList()
public AjaxResult publicList(@RequestParam(value = "lang", required = false) String lang)
{
List<HuayangCompanyTimeline> list = huayangCompanyTimelineService.selectHuayangCompanyTimelinePublicList();
applyTimelineLocale(normalizeSiteLang(lang), list);
return success(list);
}
private static String normalizeSiteLang(String lang) {
if (StringUtils.isBlank(lang)) {
return "zh_cn";
}
return lang.trim().toLowerCase();
}
private void applyTimelineLocale(String langNorm, List<HuayangCompanyTimeline> list)
{
if ("zh_cn".equals(langNorm) || list == null || list.isEmpty()) {
return;
}
List<String> keys = new ArrayList<>();
for (HuayangCompanyTimeline t : list) {
if (t.getTimelineId() == null) {
continue;
}
String id = String.valueOf(t.getTimelineId());
keys.add("company.timeline." + id + ".title");
keys.add("company.timeline." + id + ".summary");
keys.add("company.timeline." + id + ".detail");
}
Map<String, String> dict = i18nService.selectI18nValueMapByLangAndKeys(langNorm, keys);
for (HuayangCompanyTimeline t : list) {
if (t.getTimelineId() == null) {
continue;
}
String id = String.valueOf(t.getTimelineId());
String p = "company.timeline." + id + ".";
String title = dict.get(p + "title");
if (StringUtils.isBlank(title)) {
title = timelineManualField(t.getExtraData(), langNorm, "title");
}
if (StringUtils.isNotEmpty(title)) {
t.setEventTitle(title);
}
String summary = dict.get(p + "summary");
if (StringUtils.isBlank(summary)) {
summary = timelineManualField(t.getExtraData(), langNorm, "summary");
}
if (StringUtils.isNotEmpty(summary)) {
t.setEventContent(summary);
}
String detail = dict.get(p + "detail");
if (StringUtils.isBlank(detail)) {
detail = timelineManualDetailFromExtra(t.getExtraData(), langNorm);
}
if (StringUtils.isNotEmpty(detail)) {
t.setEventDetail(detail);
}
}
}
private String timelineManualField(String extraData, String langNorm, String field)
{
Object entry = getLangContentEntry(extraData, langNorm);
if (entry instanceof JSONObject) {
JSONObject o = (JSONObject) entry;
if ("summary".equals(field)) {
String s = o.getString("summary");
if (StringUtils.isBlank(s)) {
s = o.getString("content");
}
return StringUtils.isNotBlank(s) ? s : null;
}
if ("title".equals(field)) {
String s = o.getString("title");
return StringUtils.isNotBlank(s) ? s : null;
}
}
return null;
}
/** summary/detail 落空时:旧版仅存一段字符串 → 视作 detail */
private String timelineManualDetailFromExtra(String extraData, String langNorm)
{
Object entry = getLangContentEntry(extraData, langNorm);
if (entry instanceof JSONObject) {
String detail = ((JSONObject) entry).getString("detail");
return StringUtils.isNotBlank(detail) ? detail : null;
}
if (entry instanceof String) {
String s = (String) entry;
return StringUtils.isNotBlank(s) ? s : null;
}
return null;
}
private Object getLangContentEntry(String extraData, String langNorm)
{
if (StringUtils.isBlank(extraData) || StringUtils.isBlank(langNorm)) {
return null;
}
try {
JSONObject root = JSON.parseObject(extraData);
if (root == null) {
return null;
}
JSONObject langContent = root.getJSONObject("langContent");
if (langContent == null) {
return null;
}
return langContent.get(langNorm);
} catch (Exception ex) {
return null;
}
}
}
@@ -20,9 +20,8 @@ import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.common.utils.file.ImageUploadUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysUserService;
@@ -128,7 +127,7 @@ public class SysProfileController extends BaseController
if (!file.isEmpty())
{
LoginUser loginUser = getLoginUser();
String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true);
String avatar = ImageUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, true);
if (userService.updateUserAvatar(loginUser.getUserId(), avatar))
{
String oldAvatar = loginUser.getUser().getAvatar();
@@ -6,9 +6,9 @@ spring:
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost:3306/hauyang?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
url: ${DB_URL:jdbc:mysql://localhost:3306/huayang?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true}
username: ${DB_USERNAME:huayang}
password: ${DB_PASSWORD:}
# 从库数据源
slave:
# 从数据源开关/默认关闭
@@ -58,4 +58,4 @@ spring:
merge-sql: true
wall:
config:
multi-statement-allow: true
multi-statement-allow: true
@@ -0,0 +1,17 @@
# 生产环境(与 druid 数据源一起启用,例如:--spring.profiles.active=prod,druid)
# 请在服务器用环境变量或独立配置覆盖:数据源、Redis、ruoyi.profile 上传目录、token.secret 等
spring:
devtools:
restart:
enabled: false
logging:
level:
com.ruoyi: info
org.springframework: warn
springdoc:
api-docs:
enabled: false
swagger-ui:
enabled: false
@@ -7,7 +7,8 @@ ruoyi:
# 版权年份
copyrightYear: 2026
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
profile: D:/ruoyi/uploadPath
# 开发环境使用项目内目录,避免本机不存在 D:/ruoyi/uploadPath 时上传图全部 404
profile: ./uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
@@ -16,7 +17,7 @@ ruoyi:
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
port: 8080
port: 7003
servlet:
# 应用的访问路径
context-path: /
@@ -135,8 +136,7 @@ referer:
# 防盗链开关
enabled: false
# 允许的域名列表
allowed-domains: localhost,127.0.0.1,ruoyi.vip,www.ruoyi.vip
allowed-domains: localhost,127.0.0.1,192.168.1.159,ruoyi.vip,www.ruoyi.vip
# 防止XSS攻击
xss:
# 过滤开关
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="/home/ruoyi/logs" />
<property name="log.path" value="./logs" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
@@ -1 +0,0 @@
restart.include.json=/com.alibaba.fastjson2.*.jar
@@ -1,61 +0,0 @@
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost:3306/hauyang?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: ruoyi
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
@@ -1,147 +0,0 @@
# 项目相关配置
ruoyi:
# 名称
name: RuoYi
# 版本
version: 3.9.2
# 版权年份
copyrightYear: 2026
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
profile: D:/ruoyi/uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
captchaType: math
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
port: 8080
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数,默认为100
accept-count: 1000
threads:
# tomcat最大线程数,默认为200
max: 800
# Tomcat启动初始化的线程数,默认值10
min-spare: 100
# 日志配置
logging:
level:
com.ruoyi: debug
org.springframework: warn
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间(默认10分钟)
lockTime: 10
# Spring配置
spring:
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
active: druid
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
data:
# redis 配置
redis:
# 地址
host: localhost
# 端口,默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期(默认30分钟)
expireTime: 30
# MyBatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.ruoyi.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# PageHelper分页插件
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
# Springdoc配置
springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html
tags-sorter: alpha
group-configs:
- group: 'default'
display-name: '测试模块'
paths-to-match: '/**'
packages-to-scan: com.ruoyi.web.controller.tool
# 防盗链配置
referer:
# 防盗链开关
enabled: false
# 允许的域名列表
allowed-domains: localhost,127.0.0.1,ruoyi.vip,www.ruoyi.vip
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
@@ -1,24 +0,0 @@
Application Version: ${ruoyi.version}
Spring Boot Version: ${spring-boot.version}
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////
@@ -1,38 +0,0 @@
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾,访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
@@ -1,93 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="/home/ruoyi/logs" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.ruoyi" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
<!--系统用户操作日志-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 全局参数 -->
<settings>
<!-- 使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 允许JDBC 支持自动生成主键 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
</settings>
</configuration>
Loaded 100 of 1056 files, more files were not shown because too many files have changed in this diff. Show more