소스 검색

Dev cmsArticle

tsurumure 5 달 전
부모
커밋
ee5798c041

+ 13 - 5
db/cms_article.sql

@@ -10,11 +10,10 @@ CREATE TABLE `cms_article` (
     `id` BIGINT AUTO_INCREMENT COMMENT 'ID',
     `user_id` BIGINT NOT NULL COMMENT '作者/用户ID',
     `category_id` BIGINT NOT NULL COMMENT '资讯分类ID',
-    `uid` VARCHAR(36) NOT NULL COMMENT 'UID',
+    `uid` VARCHAR(36) NOT NULL UNIQUE COMMENT 'UID',
     `thumb` VARCHAR(500) COMMENT '封面图',
     `is_top` TINYINT(1) DEFAULT '-1' COMMENT '是否置顶 (-1否, 1是)',
     `status` TINYINT(1) DEFAULT '1' COMMENT '资讯状态 (-1禁用, 1启用)',
-    -- `audit_status` TINYINT(1) DEFAULT '1' COMMENT '审核状态 (-1审核拒绝, 1待审核, 2审核通过)',
     `del_flag` TINYINT(1) DEFAULT '-1' COMMENT '删除标志 (-1未删除, 1删除)',
     `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
     `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
@@ -22,7 +21,16 @@ CREATE TABLE `cms_article` (
 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='资讯表';
 
 INSERT INTO cms_article(user_id, category_id, uid, is_top) VALUES
-    (1, 3, '2a2ee08c7d', 1),
-    (1, 2, 'fdd95c7c53', -1),
-    (1, 2, '47add894f7', -1)
+   (1, 1, 'a1b2c3d4e5', -1),
+   (1, 1, 'f6g7h8i9j0', -1),
+   (1, 1, 'k1l2m3n4o5', -1),
+   (1, 1, 'p6q7r8s9t0', -1),
+   (1, 1, 'u1v2w3x4y5', -1),
+   (1, 1, 'z6a7b8c9d0', -1),
+   (1, 1, 'e1f2g3h4i5', -1),
+   (1, 1, 'j6k7l8m9n0', -1),
+   (1, 1, 'o1p2q3r4s5', -1),
+   (1, 1, 't6u7v8w9x0', -1),
+   (1, 1, 'o1p2q3r4s6', -1),
+   (1, 1, 't6u7v8w9x1', -1)
 ;

+ 30 - 6
db/cms_article_i18n.sql

@@ -1,3 +1,9 @@
+/**
+Source Server Version: 8.0.31
+Source Database: backendsys
+Date: 2025/03/29 17:09:22
+*/
+
 DROP TABLE IF EXISTS `cms_article_i18n`;
 CREATE TABLE `cms_article_i18n` (
     PRIMARY KEY (`id`),
@@ -12,10 +18,28 @@ CREATE TABLE `cms_article_i18n` (
 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='资讯表 (翻译)';
 
 INSERT INTO cms_article_i18n(article_id, language, title, description, content, meta_keyword, meta_description) VALUES
-    (1, 'zh', '时下流行的音乐节盛况', '随着夏季的到来,各种音乐节热情洋溢', '无数欢乐的人们聚集在户外,在舞台上欢呼跳跃,享受着音乐的魔力。从摇滚、电子到流行音乐,每个音乐节都有其独特的风格和魅力,成为年轻人追捧的狂欢盛事。', '音乐,舞台,年轻人,盛事', '每个音乐节都有其独特的风格和魅力,成为年轻人追捧的狂欢盛事'),
-    (1, 'en', 'The current popularity of music festivals', 'With the arrival of summer, various music festivals are in full swing', 'Countless happy people gathered outdoors, cheering and jumping on the stage, enjoying the magic of the music. From rock, electronic to pop music, each music festival has its own unique style and charm, and has become a carnival event sought after by young people.', 'Music, stage, young people, events', 'Each music festival has its own unique style and charm, and has become a carnival event sought after by young people'),
-    (2, 'zh', '阿阿2', '阿阿阿阿', '阿', 'metaKeyword-2', 'metaDescription-2'),
-    (2, 'en', 'aa2', 'aaaaaaaa', 'a', 'metaKeyword-2', 'metaDescription-2'),
-    (3, 'zh', '吧吧3', '吧吧吧吧', '吧', 'metaKeyword-3', 'metaDescription-3'),
-    (3, 'en', 'bb2', 'bbbbbbb', 'b', 'metaKeyword-3', 'metaDescription-3')
+    (1, 'zh', '夏日户外露营指南', '如何在户外享受舒适露营', '<h2>夏日户外露营指南</h2><p>夏天是露营的最佳季节,以下是一些实用的露营技巧:</p><ul><li><strong>选择合适的露营地:</strong>选择一个平坦、靠近水源且远离野生动物的地方。</li><li><strong>准备必要的装备:</strong>帐篷、睡袋、防潮垫、头灯、驱蚊剂等。</li><li><strong>注意安全:</strong>在户外要注意防火、防虫、防野生动物。</li></ul><p>希望这些技巧能帮助你在大自然中度过一个愉快的露营之旅!</p>', '露营,户外,指南', '一份全面的户外露营指南,帮助你享受大自然'),
+    (1, 'en', 'Summer Camping Guide', 'How to enjoy comfortable camping outdoors', '<h2>Summer Camping Guide</h2><p>Summer is the perfect season for camping. Here are some useful camping tips:</p><ul><li><strong>Choose the right campsite:</strong> Select a flat area near water and away from wildlife.</li><li><strong>Prepare necessary gear:</strong> Tent, sleeping bag, ground pad, headlamp, insect repellent, etc.</li><li><strong>Pay attention to safety:</strong> Be cautious of fire, insects, and wildlife while camping.</li></ul><p>Hope these tips help you enjoy a pleasant camping trip in nature!</p>', 'Camping, outdoors, guide', 'A comprehensive camping guide to help you enjoy nature'),
+    (2, 'zh', '城市夜景摄影技巧', '捕捉城市夜晚的美丽瞬间', '<h2>城市夜景摄影技巧</h2><p>城市夜景摄影可以捕捉到独特的光影效果,以下是一些技巧:</p><ul><li><strong>选择合适的拍摄地点:</strong>高楼大厦的顶部或桥梁是绝佳的拍摄点。</li><li><strong>使用三脚架:</strong>稳定相机,避免手抖影响照片质量。</li><li><strong>调整相机设置:</strong>使用低ISO、小光圈和慢快门,以获得清晰的夜景照片。</li></ul><p>通过这些技巧,你可以拍摄出令人惊叹的城市夜景照片!</p>', '摄影,夜景,技巧', '捕捉城市夜晚的美丽瞬间'),
+    (2, 'en', 'Urban Night Photography Tips', 'Capturing the beauty of the city at night', '<h2>Urban Night Photography Tips</h2><p>Capturing the beauty of urban night scenes can produce unique lighting effects. Here are some tips:</p><ul><li><strong>Choose the right shooting location:</strong> The tops of skyscrapers or bridges are excellent spots.</li><li><strong>Use a tripod:</strong> Stabilize the camera to avoid blurry photos due to hand shake.</li><li><strong>Adjust camera settings:</strong> Use low ISO, small aperture, and slow shutter speed for clear night photos.</li></ul><p>With these tips, you can capture amazing urban night photos!</p>', 'Photography, night view, tips', 'Capturing the beauty of the city at night'),
+    (3, 'zh', '春季赏花好去处', '推荐几个适合赏花的地方', '<h2>春季赏花好去处</h2><p>春天是赏花的好季节,以下是一些国内最美的赏花地点:</p><ul><li><strong>武汉大学樱花大道:</strong>每年三月,樱花盛开,如粉色云霞。</li><li><strong>婺源油菜花田:</strong>金黄的油菜花与古村落相映成趣。</li><li><strong>林芝桃花沟:</strong>桃花盛开时,漫山遍野,如诗如画。</li></ul><p>这些地方不仅能让你欣赏到美丽的花朵,还能感受春天的气息。</p>', '赏花,春季,地点', '推荐几个适合赏花的地方'),
+    (3, 'en', 'Best Flower Viewing Spots in Spring', 'Recommend some great places for flower viewing', '<h2>Best Flower Viewing Spots in Spring</h2><p>Spring is the perfect season for flower viewing. Here are some of the most beautiful flower viewing spots in the country:</p><ul><li><strong>Wuhan University Cherry Blossom Avenue:</strong> In March every year, the cherry blossoms bloom like pink clouds.</li><li><strong>Wuyuan Rapeseed Flower Fields:</strong> The golden rapeseed flowers contrast beautifully with ancient villages.</li><li><strong>Linzhi Peach Blossom Valley:</strong> When the peach blossoms bloom, the mountains are covered in a picturesque scene.</li></ul><p>These places not only allow you to enjoy beautiful flowers but also let you feel the spring atmosphere.</p>', 'Flower viewing, spring, locations', 'Recommend some great places for flower viewing'),
+    (4, 'zh', '健身新手入门指南', '如何开始你的健身之旅', '<h2>健身新手入门指南</h2><p>健身可以改善身体素质和健康状况,以下是一些入门建议:</p><ul><li><strong>设定目标:</strong>明确你的健身目标,如增肌、减脂或提高体能。</li><li><strong>制定计划:</strong>根据目标制定合理的训练和饮食计划。</li><li><strong>逐步开始:</strong>从简单的动作开始,逐渐增加难度和强度。</li></ul><p>希望这些指南能帮助你迈出健身的第一步!</p>', '健身,新手,指南', '帮助你开始健身之旅'),
+    (4, 'en', 'Fitness Beginner\'s Guide', 'How to start your fitness journey', '<h2>Fitness Beginner\'s Guide</h2><p>Fitness can improve physical fitness and health. Here are some tips for getting started:</p><ul><li><strong>Set goals:</strong> Clarify your fitness goals, such as muscle building, fat loss, or improving physical fitness.</li><li><strong>Make a plan:</strong> Create a reasonable training and diet plan based on your goals.</li><li><strong>Start gradually:</strong> Begin with simple exercises and gradually increase the difficulty and intensity.</li></ul><p>Hope these guides help you take the first step in fitness!</p>', 'Fitness, beginner, guide', 'Help you start your fitness journey'),
+    (5, 'zh', '冬季滑雪攻略', '如何享受滑雪的乐趣', '<h2>冬季滑雪攻略</h2><p>冬季是滑雪的最佳季节,以下是一些滑雪技巧:</p><ul><li><strong>选择合适的滑雪场:</strong>根据自己的水平选择适合的滑雪场。</li><li><strong>准备必要的装备:</strong>滑雪板、滑雪靴、护具、头盔等。</li><li><strong>注意安全:</strong>遵循滑雪场的规则,避免危险动作。</li></ul><p>希望这些攻略能帮助你在雪地上尽情驰骋!</p>', '滑雪,冬季,攻略', '享受滑雪的乐趣'),
+    (5, 'en', 'Winter Skiing Tips', 'How to enjoy skiing', '<h2>Winter Skiing Tips</h2><p>Winter is the perfect season for skiing. Here are some skiing tips:</p><ul><li><strong>Choose the right ski resort:</strong> Select a ski resort suitable for your skill level.</li><li><strong>Prepare necessary gear:</strong> Skis, ski boots, protective gear, helmet, etc.</li><li><strong>Pay attention to safety:</strong> Follow the ski resort rules and avoid dangerous maneuvers.</li></ul><p>Hope these tips help you enjoy skiing on the snow!</p>', 'Skiing, winter, tips', 'Enjoy skiing'),
+    (6, 'zh', '春季养生小贴士', '春季如何保持健康', '<h2>春季养生小贴士</h2><p>春季是万物复苏的季节,也是养生的好时机。以下是一些春季养生的小贴士:</p><ul><li><strong>饮食清淡:</strong>多吃蔬菜水果,少吃油腻食物。</li><li><strong>适当运动:</strong>选择适合自己的运动方式,如散步、慢跑等。</li><li><strong>保持心情舒畅:</strong>避免情绪波动,保持心态平和。</li></ul><p>希望这些小贴士能帮助你度过一个健康的春季!</p>', '春季,养生,健康', '春季养生小贴士,帮助你保持健康'),
+    (6, 'en', 'Spring Health Tips', 'How to stay healthy in spring', '<h2>Spring Health Tips</h2><p>Spring is the season of renewal and a great time for health maintenance. Here are some tips for staying healthy in spring:</p><ul><li><strong>Eat lightly:</strong> Focus on vegetables and fruits, and avoid greasy foods.</li><li><strong>Exercise moderately:</strong> Choose activities like walking or jogging.</li><li><strong>Stay in good spirits:</strong> Avoid emotional fluctuations and maintain a peaceful mindset.</li></ul><p>Hope these tips help you have a healthy spring!</p>', 'Spring, health, tips', 'Spring health tips to keep you in good shape'),
+    (7, 'zh', '家庭园艺入门指南', '如何打造自己的小花园', '<h2>家庭园艺入门指南</h2><p>园艺不仅能美化环境,还能带来乐趣。以下是一些家庭园艺的入门建议:</p><ul><li><strong>选择合适的植物:</strong>根据光照条件选择适合的植物。</li><li><strong>准备基本工具:</strong>铲子、喷壶、花盆等。</li><li><strong>定期浇水和施肥:</strong>保持土壤湿润,适时补充养分。</li></ul><p>希望这些指南能帮助你打造一个美丽的小花园!</p>', '园艺,家庭,入门', '家庭园艺入门指南,打造你的小花园'),
+    (7, 'en', 'Home Gardening Beginner\'s Guide', 'How to create your own little garden', '<h2>Home Gardening Beginner\'s Guide</h2><p>Gardening not only beautifies the environment but also brings joy. Here are some tips for getting started with home gardening:</p><ul><li><strong>Choose the right plants:</strong> Select plants based on your light conditions.</li><li><strong>Prepare basic tools:</strong> Trowel, watering can, pots, etc.</li><li><strong>Water and fertilize regularly:</strong> Keep the soil moist and add nutrients as needed.</li></ul><p>Hope these guides help you create a beautiful little garden!</p>', 'Gardening, home, beginner', 'Home gardening guide to create your own garden'),
+    (8, 'zh', '夏季防晒全攻略', '如何有效防晒', '<h2>夏季防晒全攻略</h2><p>夏季阳光强烈,防晒至关重要。以下是一些防晒建议:</p><ul><li><strong>选择合适的防晒霜:</strong>SPF 30 以上,PA+++ 为佳。</li><li><strong>定时补涂:</strong>每 2-3 小时补涂一次。</li><li><strong>避免长时间暴晒:</strong>尽量减少在紫外线最强时段外出。</li></ul><p>希望这些攻略能帮助你有效防晒,享受夏日阳光!</p>', '防晒,夏季,攻略', '夏季防晒全攻略,保护你的皮肤'),
+    (8, 'en', 'Summer Sun Protection Guide', 'How to protect your skin from the sun', '<h2>Summer Sun Protection Guide</h2><p>With intense summer sun, sun protection is essential. Here are some tips:</p><ul><li><strong>Choose the right sunscreen:</strong> SPF 30 or higher, PA+++ is recommended.</li><li><strong>Reapply regularly:</strong> Every 2-3 hours.</li><li><strong>Avoid prolonged exposure:</strong> Minimize outdoor activities during peak UV hours.</li></ul><p>Hope these tips help you enjoy the summer sun while staying protected!</p>', 'Sun protection, summer, guide', 'Summer sun protection guide to keep your skin safe'),
+    (9, 'zh', '秋季美食推荐', '秋季不可错过的美食', '<h2>秋季美食推荐</h2><p>秋季是丰收的季节,有许多美味的食物值得一试:</p><ul><li><strong>大闸蟹:</strong>肉质鲜美,营养丰富。</li><li><strong>栗子:</strong>香甜可口,适合烤制或煮食。</li><li><strong>南瓜:</strong>口感软糯,可做多种菜肴。</li></ul><p>这些美食不仅能满足味蕾,还能补充营养,让你享受秋季的丰收喜悦!</p>', '美食,秋季,推荐', '秋季美食推荐,享受丰收的美味'),
+    (9, 'en', 'Autumn Food Recommendations', 'Delicious foods to enjoy in autumn', '<h2>Autumn Food Recommendations</h2><p>Autumn is the season of harvest, with many delicious foods to try:</p><ul><li><strong>Crab:</strong> Tender and nutritious.</li><li><strong>Chestnuts:</strong> Sweet and delicious, perfect for roasting or boiling.</li><li><strong>Pumpkin:</strong> Soft and versatile, great for various dishes.</li></ul><p>These foods not only satisfy your taste buds but also provide nutrition, letting you enjoy the joy of autumn harvest!</p>', 'Food, autumn, recommendations', 'Autumn food recommendations to enjoy the harvest'),
+    (10, 'zh', '冬季保暖小妙招', '如何在寒冷中保持温暖', '<h2>冬季保暖小妙招</h2><p>冬季寒冷,保暖很重要。以下是一些保暖小妙招:</p><ul><li><strong>穿多层衣物:</strong>增加保暖效果。</li><li><strong>戴帽子和围巾:</strong>减少头部热量散失。</li><li><strong>保持室内温暖:</strong>使用暖气或电热毯。</li></ul><p>希望这些小妙招能帮助你在寒冷的冬季保持温暖!</p>', '保暖,冬季,小妙招', '冬季保暖小妙招,让你温暖过冬'),
+    (10, 'en', 'Winter Warmth Tips', 'How to stay warm in the cold', '<h2>Winter Warmth Tips</h2><p>Winter is cold, and staying warm is crucial. Here are some tips:</p><ul><li><strong>Wear multiple layers:</strong> Enhance warmth.</li><li><strong>Wear hats and scarves:</strong> Reduce heat loss from the head.</li><li><strong>Keep indoor warmth:</strong> Use heating or electric blankets.</li></ul><p>Hope these tips help you stay warm in the cold winter!</p>', 'Warmth, winter, tips', 'Winter warmth tips to keep you cozy'),
+    (11, 'zh', '春季户外徒步指南', '如何享受徒步的乐趣', '<h2>春季户外徒步指南</h2><p>春季是徒步的好季节,以下是一些徒步建议:</p><ul><li><strong>选择合适的路线:</strong>根据自己的体力选择合适的徒步路线。</li><li><strong>准备必要的装备:</strong>舒适的徒步鞋、背包、水和食物。</li><li><strong>注意安全:</strong>告知他人你的行程,避免独自前往危险区域。</li></ul><p>希望这些指南能帮助你享受徒步的乐趣!</p>', '徒步,春季,指南', '春季户外徒步指南,享受徒步的乐趣'),
+    (11, 'en', 'Spring Hiking Guide', 'How to enjoy hiking', '<h2>Spring Hiking Guide</h2><p>Spring is a great season for hiking. Here are some tips:</p><ul><li><strong>Choose the right trail:</strong> Select a trail based on your physical condition.</li><li><strong>Prepare necessary gear:</strong> Comfortable hiking shoes, backpack, water, and food.</li><li><strong>Pay attention to safety:</strong> Inform someone of your itinerary and avoid dangerous areas alone.</li></ul><p>Hope these guides help you enjoy hiking!</p>', 'Hiking, spring, guide', 'Spring hiking guide to enjoy the outdoors'),
+    (12, 'zh', '家居收纳技巧大全', '如何打造整洁有序的家', '<h2>家居收纳技巧大全</h2><p>一个整洁有序的家能让人感到舒适和放松。以下是一些实用的家居收纳技巧:</p><ul><li><strong>分类整理:</strong>将物品按照类别进行分类,方便查找。</li><li><strong>利用收纳工具:</strong>如收纳盒、挂钩、置物架等。</li><li><strong>定期清理:</strong>定期检查并清理不需要的物品。</li></ul><p>希望这些技巧能帮助你打造一个整洁有序的家!</p>', '家居,收纳,技巧', '家居收纳技巧大全,打造整洁有序的家'),
+    (12, 'en', 'Home Organization Tips', 'How to create a tidy and organized home', '<h2>Home Organization Tips</h2><p>A tidy and organized home can bring comfort and relaxation. Here are some practical tips for home organization:</p><ul><li><strong>Sort and categorize:</strong> Organize items by category for easy access.</li><li><strong>Use storage tools:</strong> Such as storage boxes, hooks, and shelves.</li><li><strong>Regular decluttering:</strong> Periodically check and remove items you no longer need.</li></ul><p>Hope these tips help you create a tidy and organized home!</p>', 'Home, organization, tips', 'Home organization tips to keep your space tidy')
 ;

+ 23 - 0
src/main/java/com/backendsys/config/Locale/CustomCookieLocaleResolver.java

@@ -0,0 +1,23 @@
+package com.backendsys.config.Locale;
+
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.web.servlet.i18n.CookieLocaleResolver;
+import org.springframework.web.util.WebUtils;
+
+import java.util.Locale;
+
+public class CustomCookieLocaleResolver extends CookieLocaleResolver {
+    @Override
+    public Locale resolveLocale(HttpServletRequest request) {
+        // 从Cookie中获取lang值
+        Cookie cookie = WebUtils.getCookie(request, "lang");
+        if (cookie != null) {
+            String lang = cookie.getValue();
+            if ("en".equals(lang) || "zh".equals(lang)) {
+                return Locale.forLanguageTag(lang);
+            }
+        }
+        return super.resolveLocale(request);
+    }
+}

+ 20 - 0
src/main/java/com/backendsys/config/Locale/LocaleConfig.java

@@ -0,0 +1,20 @@
+package com.backendsys.config.Locale;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
+
+@Configuration
+public class LocaleConfig implements WebMvcConfigurer {
+    @Bean
+    public LocaleResolver localeResolver() {
+        return new CustomCookieLocaleResolver();
+    }
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(new LocaleChangeInterceptor());
+    }
+}

+ 1 - 0
src/main/java/com/backendsys/modules/cms/article/dao/ArticleDao.java

@@ -11,4 +11,5 @@ import java.util.Map;
 public interface ArticleDao extends BaseMapper<Article> {
     List<Article> selectArticleList(Article article);
     Map<String, Object> selectArticleDetail(Article article);
+//    Article selectArticle(Article article);
 }

+ 9 - 0
src/main/java/com/backendsys/modules/cms/article/entity/Article.java

@@ -34,8 +34,17 @@ public class Article {
     @TableField(exist = false)
     @NotNull(message="article_id 不能为空", groups = { Delete.class })
     private List<Long> article_ids;
+
     @TableField(exist = false)
     private String title;
+    @TableField(exist = false)
+    private String description;
+    @TableField(exist = false)
+    private String content;
+    @TableField(exist = false)
+    private String meta_keyword;
+    @TableField(exist = false)
+    private String meta_description;
 
     @TableField(exist = false)
     @NotEmpty(message="内容不能为空", groups = { Create.class, Update.class})

+ 4 - 0
src/main/java/com/backendsys/modules/cms/article/service/ArticleService.java

@@ -11,6 +11,10 @@ public interface ArticleService {
     PageEntity selectArticleList(Article article);
     // 获取资讯详情
     Map<String, Object> selectArticleDetail(Article article);
+
+    // 获取资讯详情 (指定)
+    Article selectArticle(String lang, String uid);
+
     // 创建资讯
     Map<String, Object> insertArticle(Article article);
     // 编辑资讯

+ 29 - 0
src/main/java/com/backendsys/modules/cms/article/service/impl/ArticleServiceImpl.java

@@ -12,6 +12,7 @@ import com.backendsys.modules.common.utils.ValidationUtil;
 import com.backendsys.utils.response.PageEntity;
 import com.backendsys.utils.response.PageInfoResult;
 import com.backendsys.utils.v2.PageUtils;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -53,6 +54,34 @@ public class ArticleServiceImpl implements ArticleService {
         return detail;
     }
 
+    /**
+     * 获取资讯详情 (公开)
+     */
+    @Override
+    public Article selectArticle(String lang, String uid) {
+
+        // [Get] 详情
+        Article detail = articleDao.selectOne(new LambdaQueryWrapper<Article>().eq(Article::getUid, uid));
+        if (detail == null) return null;
+
+        // [Get] 翻译
+        LambdaQueryWrapper<ArticleI18n> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(ArticleI18n::getLanguage, lang);
+        wrapper.eq(ArticleI18n::getArticle_id, detail.getId());
+        ArticleI18n i18nDetail = articleI18nDao.selectOne(wrapper);
+        if (i18nDetail == null) return null;
+
+        // [拼接] 详情 + 翻译
+        detail.setTitle(i18nDetail.getTitle());
+        detail.setDescription(i18nDetail.getDescription());
+        detail.setContent(i18nDetail.getContent());
+        detail.setMeta_keyword(i18nDetail.getMeta_keyword());
+        detail.setMeta_description(i18nDetail.getMeta_description());
+
+        return detail;
+    }
+
+
     /**
      * 创建资讯
      */

+ 30 - 47
src/main/java/com/backendsys/modules/cms/template/ViewArticleController.java

@@ -1,5 +1,6 @@
 package com.backendsys.modules.cms.template;
 
+import cn.hutool.core.convert.Convert;
 import com.backendsys.modules.cms.article.dao.ArticleDao;
 import com.backendsys.modules.cms.article.entity.Article;
 import com.backendsys.modules.cms.article.service.ArticleService;
@@ -8,12 +9,16 @@ import com.backendsys.modules.common.utils.CookieUtil;
 import com.backendsys.utils.response.PageEntity;
 import com.backendsys.utils.v2.PageUtils;
 import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import java.util.Map;
+
 @Controller
 public class ViewArticleController {
 
@@ -21,40 +26,26 @@ public class ViewArticleController {
     private ArticleService articleService;
 
     @Pages
-    @GetMapping({"/article"})
-    public String article(Model model, HttpServletRequest request,
+    @GetMapping({"/article/list"})
+    public String article(Model model,
         @RequestParam(value = "page_num", defaultValue = "1") Integer pageNum,
         @RequestParam(value = "page_size", defaultValue = "10") Integer pageSize,
         @RequestParam(value = "title", required = false) String title,
         @RequestParam(value = "category_id", required = false) Long categoryId
     ) {
 
-        model.addAttribute("title","资讯中心");
-
-//        // -- 初始化公共事件 ---------------------------------------------
-//        RequestUtil.initResponse(model, request, cmsSiteInfoService, cmsNavigationService);
-        String lang = CookieUtil.getCookie("lang", request);
-        System.out.println("lang = " + lang);
-//        // -- Header ---------------------------------------------
-//        model.addAttribute("title", "zh".equals(lang) ? "资讯中心": "Aricle");
-//        // -------------------------------------------------------
-//
-//        // [Get] 资讯列表
-//        CmsArticleDTO cmsArticleDTO = new CmsArticleDTO();
-//        cmsArticleDTO.setTitle(title);
-//        cmsArticleDTO.setCategory_id(categoryId);
-//        cmsArticleDTO.setLang(lang);
+        String lang = Convert.toStr(model.getAttribute("lang"));
 
+        // [Get] 获取资讯列表
         Article article = new Article();
         article.setLang(lang);
         article.setTitle(title);
         article.setCategory_id(categoryId);
-
-        PageUtils.startPage();  // 分页
+        //
+        PageUtils.startPage(pageNum, pageSize);  // 分页
         PageEntity articleList = articleService.selectArticleList(article);
+        model.addAttribute("articleList", articleList);
 
-        System.out.println(articleList);
-        model.addAttribute("article", articleList);
 //
 //        // [Get] 资讯分类
 //        List<Map<String, Object>> categoryList = articleCategoryService.queryArticleCategoryActive();
@@ -82,37 +73,29 @@ public class ViewArticleController {
 //        parameters.put("category_name", categoryName);
 //        model.addAttribute("parameters", parameters);
 
-        System.out.println("article layout!");
-
         // -- Layout ---------------------------------------------
+        model.addAttribute("title", "zh".equals(lang) ? "资讯中心": "Aricle");
         model.addAttribute("layout", "article");
         return "layout/layout";
     }
 
 
-//    @GetMapping({"/articleDetail/{id}"})
-//    public String articleDetail(Model model, HttpServletRequest request, @PathVariable("id") Long id) {
-//
-//        // -- 初始化公共事件 ---------------------------------------------
-//        RequestUtil.initResponse(model, request, cmsSiteInfoService, cmsNavigationService);
-//        // -- Header ---------------------------------------------
-//        model.addAttribute("title","资讯中心");
-//        // -------------------------------------------------------
-//
-//        // [Get] 资讯详情
-//        CmsArticleDTO cmsArticleDTO = new CmsArticleDTO();
-//        cmsArticleDTO.setArticle_id(id);
-//        cmsArticleDTO.setLang((String) model.getAttribute("lang"));
-//
-//        Map<String, Object> detail = articleService.queryArticleDetailPublic(cmsArticleDTO);
-//        model.addAttribute("articleDetail", detail);
-//
-//        // [Get] 资讯分类
-//        model.addAttribute("articleCategory", articleCategoryService.queryArticleCategoryActive());
-//
-//        // -- Layout ---------------------------------------------
-//        model.addAttribute("layout", "articleDetail");
-//        return "layout/layout";
-//    }
+    @Pages
+    @GetMapping({"/articleDetail/{uid}"})
+    public String articleDetail(Model model, @PathVariable("uid") String uid) {
+
+        String lang = Convert.toStr(model.getAttribute("lang"));
+
+        // [Get] 获取资讯详情
+        Article articleDetail = articleService.selectArticle(lang, uid);
+        if (articleDetail == null) return "error";
+
+        model.addAttribute("articleDetail", articleDetail);
+
+        // -- Layout ---------------------------------------------
+        model.addAttribute("title", articleDetail.getTitle() + "- 资讯详情");
+        model.addAttribute("layout", "articleDetail");
+        return "layout/layout";
+    }
 
 }

+ 3 - 0
src/main/java/com/backendsys/modules/cms/template/ViewIndexController.java

@@ -1,5 +1,6 @@
 package com.backendsys.modules.cms.template;
 
+import cn.hutool.core.convert.Convert;
 import com.backendsys.modules.common.aspect.Pages;
 import jakarta.servlet.http.HttpServletRequest;
 import org.springframework.stereotype.Controller;
@@ -13,6 +14,8 @@ public class ViewIndexController {
     @GetMapping({"/"})
     public String index(Model model, HttpServletRequest request) {
 
+        String lang = Convert.toStr(model.getAttribute("lang"));
+
         model.addAttribute("title","首页");
 
 //        model.addAttribute("bannerList", cmsBannerService.queryBannerPublic());

+ 6 - 10
src/main/java/com/backendsys/modules/common/aspect/PagesAspect.java

@@ -45,31 +45,27 @@ public class PagesAspect {
 
 		HttpServletRequest request = httpRequestUtil.getRequest();
 
-//		// 获取注解上的参数
-//		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
-//		Method method = signature.getMethod();
-//		Pages params = method.getAnnotation(Pages.class);
-
 		// 从 JoinPoint 获取控制器方法的参数
 		Object[] args = joinPoint.getArgs();
 		for (Object arg : args) {
 			if (arg instanceof Model) {
 				Model model = (Model) arg;
 
-				// [Cookie] 获得语种 (默认是:中文)
-				String default_lang = "zh";
+				// [Cookie] 获取/设置默认语言 (默认是:中文)
 				String lang = CookieUtil.getCookie("lang", request);
+				String default_lang = "zh";
 				if (StrUtil.isEmpty(lang)) {
 					lang = default_lang;
 					CookieUtil.setCookie("lang", lang);
 				}
-				System.out.println("(PagesAspect) lang = " + lang);
-				
+				request.setAttribute("lang", lang);
+
+				model.addAttribute("lang", lang);
+
 				// [Get] 获取站点信息
 				model.addAttribute("siteInfo", siteInfoService.selectSiteInfo());
 				// [Get] 获取导航
 				model.addAttribute("navigation", navigationService.selectNavigationTemplate(lang));
-
 				// 其他信息
 				model.addAttribute("timestamp", DateUtil.current());			// 当前时间戳
 				model.addAttribute("request_uri", request.getRequestURI());				// 当前访问路径

+ 9 - 2
src/main/java/com/backendsys/utils/v2/PageUtils.java

@@ -5,8 +5,7 @@ import com.github.pagehelper.PageHelper;
 /**
  * 分页工具类
  */
-public class PageUtils extends PageHelper
-{
+public class PageUtils extends PageHelper {
     /**
      * 设置请求分页数据
      */
@@ -20,6 +19,14 @@ public class PageUtils extends PageHelper
             PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
         }
     }
+    public static void startPage(Integer pageNum, Integer pageSize) {
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
+        Boolean reasonable = pageDomain.getReasonable();
+        if (pageNum != null && pageSize != null) {
+            PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
+        }
+    }
 
     /**
      * 清理分页的线程变量

+ 1 - 1
src/main/resources/__templates/__article.html

@@ -14,7 +14,7 @@
 
           <!-- 搜索框 -->
           <div class="sub-search">
-            <div th:replace="~{components/search-input::search-input}"></div>
+            <div th:replace="~{components/article-search-input::search-input}"></div>
           </div>
 
           <div class="article-list" th:classappend="${isBrand ? 'column' : ''}">

+ 1 - 1
src/main/resources/__templates/index.html

@@ -23,7 +23,7 @@
     </script>
     <!-- 搜索框 -->
     <div class="index-search-bar">
-      <div th:replace="~{components/search-input::search-input}"></div>
+      <div th:replace="~{components/article-search-input::search-input}"></div>
     </div>
   </div>
 

+ 4 - 5
src/main/resources/application.yml

@@ -12,6 +12,9 @@ spring:
     multipart:
       max-file-size: 5GB               # 上传文件大小限制
       max-request-size: 5GB
+  messages:
+    basename: i18n/locale
+    encoding: UTF-8
   mvc:
     async:
       request-timeout: 600000           # 接口超时时间,默认 60 秒
@@ -25,8 +28,7 @@ spring:
 #          max-idle: 8       # 最大空闲连接,默认8
 #          min-idle: 0       # 最小空闲连接,默认0
 
-  messages:
-    basename: i18n/locale
+
 
   web:
     resources:
@@ -52,9 +54,6 @@ spring:
     servlet:
       content-type: text/html
 
-#  messages:
-#    basename: i18n/locale
-
 
 
 

+ 1 - 1
src/main/resources/templates/__article.html

@@ -14,7 +14,7 @@
 
           <!-- 搜索框 -->
           <div class="sub-search">
-            <div th:replace="~{components/search-input::search-input}"></div>
+            <div th:replace="~{components/article-search-input::search-input}"></div>
           </div>
 
           <div class="article-list" th:classappend="${isBrand ? 'column' : ''}">

+ 62 - 0
src/main/resources/templates/_articleDetail.html

@@ -0,0 +1,62 @@
+<div class="hjcl-layout-container" th:fragment="body">
+
+  <!-- 内页 幻灯片区域 -->
+  <div th:replace="~{components/sub-banner::sub-banner}"></div>
+
+  <!-- 内容 -->
+  <div class="sub-section-main">
+    <div class="wrapper">
+      <div class="sub-section-container">
+
+        <div class="article-detail">
+          <div class="sub-section-container-title">
+            <div class="article-detail-title" th:text="${articleDetail.title}"></div>
+            <div class="article-detail-subtitle">
+                <div th:text="${articleDetail.create_time}"></div>
+                <a class="back" href="javascript:history.back();" th:text="#{back}"></a>
+            </div>
+          </div>
+          <div class="sub-section-container-content bg-white" style="padding: 20px; box-sizing: border-box">
+            <div class="sub-section-container-description" th:text="${articleDetail.description}"></div>
+            <div class="content-detail" >
+              <div th:utext="${articleDetail.content}"></div>
+            </div>
+          </div>
+        </div>
+
+      </div>
+
+      <!-- 侧边 -->
+      <div class="sub-section-sider">
+        <div class="sub-section-sider-title" th:text="#{category}"></div>
+        <div class="sub-section-sider-content">
+          <div class="article-category horizontal">
+            <ul id="category-bar">
+<!--              <li class="mb-4"><a href="javascript:;">全部</a></li>-->
+              <li th:each="item, itemStat : ${articleCategory}" th:if="${item.is_brand == 1}">
+                <a href="javascript:;" th:text="${lang} == 'zh' ? ${item.category_name} : ${item.category_name_en}" th:data-category-id="${item.article_category_id}"></a>
+              </li>
+            </ul>
+          </div>
+        </div>
+        <div class="sub-section-sider-content mt-2">
+            <p th:text="#{sub.sider.welcome}"></p>
+            <p th:text="@{#{sub.sider.wechat} + ' cpnana8 / anywayto'}"></p>
+        </div>
+      </div>
+      <script th:inline="javascript">
+          // [Click] 分类
+          $('#category-bar > li > a').on('click', function(){
+              var category_id = $(this).attr('data-category-id')
+              if (category_id) {
+                  window.location.href = '/article?category_id=' + parseInt(category_id)
+              } else {
+                  window.location.href = '/article';
+              }
+          })
+      </script>
+
+    </div>
+  </div>
+
+</div>

+ 8 - 8
src/main/resources/templates/article.html

@@ -3,25 +3,25 @@
   <div class="sub-section-main">
     <div class="wrapper">
       <div class="sub-section-container">
-        <div class="sub-section-container-title">Category</div>
+        <div class="sub-section-container-title" th:text="${title}"></div>
         <div class="sub-section-container-content">
 
           <div class="sub-search">
-            <div th:replace="~{components/search-input::search-input}"></div>
+            <div th:replace="~{components/article-search-input::article-search-input}"></div>
           </div>
 
           <div class="article-list">
-            <div class="article-item" th:each="item, itemStat : ${article.list}">
-              <a class="article-thumb" th:if="${item.thumb != ''}" th:href="@{'/articleDetail/' + ${item.id}}"><img th:src="${item.thumb}" /></a>
+            <div class="article-item" th:each="item, itemStat : ${articleList.list}">
+              <a class="article-thumb" th:if="${item.thumb != ''}" th:href="@{'/articleDetail/' + ${item.uid}}"><img th:src="${item.thumb}" /></a>
               <div class="article-info">
-                <a class="article-info-title" th:href="@{'/articleDetail/' + ${item.id}}">
+                <a class="article-info-title" th:href="@{'/articleDetail/' + ${item.uid}}">
                   <span th:text="${item.title}"></span>
                   <span th:if="${item.is_top == 1}" th:text="@{'[' + #{settop} + ']'}"></span>
                 </a>
                 <div class="article-info-description" th:text="${item.description}"></div>
                 <div class="article-info-bottom">
                   <div class="article-info-time" th:text="${item.create_time}"></div>
-                  <a class="article-info-btn" th:href="@{'/articleDetail/' + ${item.id}}" th:text="#{viewdetail}"></a>
+                  <a class="article-info-btn" th:href="@{'/articleDetail/' + ${item.uid}}" th:text="#{viewdetail}"></a>
                 </div>
               </div>
             </div>
@@ -35,9 +35,9 @@
                 elem: 'pagination-bar', theme: '#333e46',
                 prev: (lang == 'zh' ? '上一页' : 'Prev'),
                 next: (lang == 'zh' ? '下一页' : 'Next'),
-                count: [[${article.total}]], curr: [[${article.page_num}]], limit: [[${article.page_size}]],
+                count: [[${articleList.total}]], curr: [[${articleList.page_num}]], limit: [[${articleList.page_size}]],
                 jump: function(obj, first) {
-                  // console.log(obj)
+                  console.log(obj)
                   // 首次不执行
                   if (!first) {
                     //do something

+ 0 - 37
src/main/resources/templates/articleDetail.html

@@ -19,49 +19,12 @@
           <div class="sub-section-container-content bg-white" style="padding: 20px; box-sizing: border-box">
             <div class="sub-section-container-description" th:text="${articleDetail.description}"></div>
             <div class="content-detail" >
-
-              <div th:if="${articleDetail.video != null && articleDetail.video != ''}" class="article-video-bar">
-                <video class="article-video" th:src="${articleDetail.video}" controls></video>
-              </div>
-
               <div th:utext="${articleDetail.content}"></div>
-
             </div>
           </div>
         </div>
 
       </div>
-
-      <!-- 侧边 -->
-      <div class="sub-section-sider">
-        <div class="sub-section-sider-title" th:text="#{category}"></div>
-        <div class="sub-section-sider-content">
-          <div class="article-category horizontal">
-            <ul id="category-bar">
-<!--              <li class="mb-4"><a href="javascript:;">全部</a></li>-->
-              <li th:each="item, itemStat : ${articleCategory}" th:if="${item.is_brand == 1}">
-                <a href="javascript:;" th:text="${lang} == 'zh' ? ${item.category_name} : ${item.category_name_en}" th:data-category-id="${item.article_category_id}"></a>
-              </li>
-            </ul>
-          </div>
-        </div>
-        <div class="sub-section-sider-content mt-2">
-            <p th:text="#{sub.sider.welcome}"></p>
-            <p th:text="@{#{sub.sider.wechat} + ' cpnana8 / anywayto'}"></p>
-        </div>
-      </div>
-      <script th:inline="javascript">
-          // [Click] 分类
-          $('#category-bar > li > a').on('click', function(){
-              var category_id = $(this).attr('data-category-id')
-              if (category_id) {
-                  window.location.href = '/article?category_id=' + parseInt(category_id)
-              } else {
-                  window.location.href = '/article';
-              }
-          })
-      </script>
-
     </div>
   </div>
 

+ 3 - 3
src/main/resources/templates/components/search-input.html → src/main/resources/templates/components/article-search-input.html

@@ -1,4 +1,4 @@
-<div th:fragment="search-input">
+<div th:fragment="article-search-input">
 
   <div class="index-search">
     <input class="index-search-input" th:value="${parameters != null ? parameters.title : ''}" id="searchInput" type="text"
@@ -9,9 +9,9 @@
     $('#searchBtn').on('click', function() {
         var val = $('#searchInput').val()
         if (val) {
-          window.location.href = '/article?title=' + val
+          window.location.href = '/article/list?title=' + val
         } else {
-          window.location.href = '/article'
+          window.location.href = '/article/list'
         }
     })
     $('#searchInput').on('keydown', function(e) {