<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>寻径</title><description>向内探求，静水深流</description><link>https://blog.zhenxuna.cn/</link><language>zh_CN</language><item><title>一次 Intel iGPU 硬件编码失效的排查：GuC / HuC / firmware 才是关键</title><link>https://blog.zhenxuna.cn/post/11/</link><guid isPermaLink="true">https://blog.zhenxuna.cn/post/11/</guid><description>记录一次在 Debian 13 上部署 Jellyfin 时，硬件转码“看起来一切正常，但就是不能用”的完整排错过程。</description><pubDate>Fri, 30 Jan 2026 13:47:26 GMT</pubDate><content:encoded>&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;鄙人最近开始优化Homelab架构，打算做算存分离，遂把Jellyfin等容器开始移向我的N5095小主机上，但存在无法硬件编码的情况，排查了一下午，换了许多的镜像都无法成功，故折腾了一翻，才有了这篇文章，旨在帮助后人在再次遇到此类问题时能有些许帮助&lt;/p&gt;
&lt;h2&gt;正文&lt;/h2&gt;
&lt;h3&gt;环境&lt;/h3&gt;
&lt;p&gt;硬件平台：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU：Intel Jasper Lake N5095（Gen11 iGPU）&lt;/li&gt;
&lt;li&gt;无独显&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;系统环境：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Debian 13 (trixie)&lt;/li&gt;
&lt;li&gt;Kernel: 6.12.x&lt;/li&gt;
&lt;li&gt;Docker + docker-compose&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;服务：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jellyfin（nyanmisaka/jellyfin）&lt;/li&gt;
&lt;li&gt;使用 QSV / VAAPI 硬件转码&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;现象描述&lt;/h3&gt;
&lt;p&gt;鄙人在转移服务时，发现同一套Jellyfin配置下，在QNAP-464C2（同样也是N5095）上可以正常运行硬件编码，但是在Debian13上无法播放，FFmpeg日志中只有一句：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;FFmpeg exited with code 234
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在尝试关闭硬件编码后，一切正常，但性能很烂，QSV解码+软件编码在4K80Mbps HDR10 转码20Mbps SDR色调映射时转码帧率只有20，而且CPU打满影响其他容器，这肯定是无法满足正常需求的
已经确定的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;QSV解码能正常运行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/dri/renderD128&lt;/code&gt;在容器中是存在的&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vainfo&lt;/code&gt;在容器中也是正常的&lt;/li&gt;
&lt;li&gt;&lt;code&gt;intel_gpu_top&lt;/code&gt;也能正常看到有调用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一切都指向硬件解码是完好的，但硬件编码就是无法正常使用&lt;/p&gt;
&lt;h3&gt;突破点&lt;/h3&gt;
&lt;p&gt;我在QNAP上排查到这一步时：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo dmesg | grep -i guc
sudo dmesg | grep -i huc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果中有部分是这样的:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[    5.960585] [drm] GuC communication enabled
[    5.971545] i915 0000:00:02.0: [drm] GuC firmware i915/ehl_guc_33.0.4.bin version 33.0 submission:disabled
[    5.982316] i915 0000:00:02.0: [drm] HuC firmware i915/ehl_huc_9.0.0.bin version 9.0 authenticated:yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我在Debian上敲了一样的命令，发现什么都没有，感觉完全没有加载
查询了一下，Debian没有安装Intel的非自由固件&lt;/p&gt;
&lt;h3&gt;解决方案&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;安装固件包&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install firmware-misc-nonfree firmware-intel-misc
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;装完后查看&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;ls /lib/firmware/i915/ | grep ehl
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应当能看到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ehl_guc_70.1.1.bin
ehl_huc_9.0.0.bin
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;修改GRUB，添加&lt;code&gt;i915.enable_guc=3&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;GRUB_CMDLINE_LINUX_DEFAULT=&quot;quiet i915.enable_guc=3&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;更新GRUB并重启&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;sudo update-grub
sudo reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;重启后再次查看dmesg，应当看到类似以下的内容&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;[   34.982106] Setting dangerous option enable_guc - tainting kernel
[   36.726000] i915 0000:00:02.0: [drm] GT0: GuC firmware i915/ehl_guc_70.1.1.bin version 70.1.1
[   36.742212] i915 0000:00:02.0: [drm] GT0: GUC: submission enabled
[   36.742214] i915 0000:00:02.0: [drm] GT0: GUC: SLPC disabled
[   36.726021] i915 0000:00:02.0: [drm] GT0: HuC firmware i915/ehl_huc_9.0.0.bin version 9.0.0
[   36.741869] i915 0000:00:02.0: [drm] GT0: HuC: authenticated for all workloads
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Jellyfin配置参考&lt;/h3&gt;
&lt;p&gt;docker-compose.yml:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  jellyfin:
    image: nyanmisaka/jellyfin:latest
    container_name: Jellyfin
    privileged: true
    network_mode: host
    restart: unless-stopped
    devices:
      - /dev/dri/renderD128:/dev/dri/renderD128
    volumes:
      - ./config:/config
      - ./cache:/cache
      - ./media:/media
      - /etc/localtime:/etc/localtime:ro
    environment:
      - TZ=Asia/Shanghai
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;jellyfin:
&lt;img src=&quot;/assets/media/2cc7b62569eb1bb1811885ae7d87be03755ebd7dccaad7f2d7372d0a020bb6ea-1920.webp&quot; alt=&quot;图片.png&quot; /&gt;
&lt;img src=&quot;/assets/media/c52fa0a5dc2f067ab37dbe520f2e8238fe7f7a589ac5410d2badf3623d8b198f-1920.webp&quot; alt=&quot;图片.png&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>自定义注解鉴权</title><link>https://blog.zhenxuna.cn/post/10/</link><guid isPermaLink="true">https://blog.zhenxuna.cn/post/10/</guid><description>在AOP中使用自定义注解鉴权</description><pubDate>Mon, 03 Feb 2025 08:01:21 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;在Service层里鉴权固然方便，也是我一直使用的方式，但是，如果我需要级联调用service，就会导致重复鉴权，这一点是没必要的，所以打算把鉴权提取出来。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;开始&lt;/h2&gt;
&lt;h3&gt;1. 注解&lt;/h3&gt;
&lt;p&gt;创建一个注解类&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
public @interface RequirePermission {
    Role[] value();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意这个Role是一个权限的枚举类&lt;/p&gt;
&lt;h3&gt;2. 在Controller打上注解&lt;/h3&gt;
&lt;p&gt;可以在类上注解&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@RequirePermission({Role.systemAdministrator})
public class UniversityController 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以在方法上注解&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    @RequirePermission({Role.systemAdministrator})
    @ApiOperation(&quot;登陆接口&quot;)
    @PostMapping(&quot;/login&quot;)
    public Response&amp;lt;LoginResultVO&amp;gt; login(@RequestBody LoginForm loginForm) 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 在AOP中解析&lt;/h3&gt;
&lt;p&gt;定义切点&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    @Pointcut(&quot;@within(*.api.annotation.RequirePermission) || @annotation(*.api.annotation.RequirePermission)&quot;)
    public void permissionCheck() {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;定义方法&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    @Around(&quot;permissionCheck()&quot;)
    public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        RequirePermission methodAnnotation = AnnotationUtils.findAnnotation(method, RequirePermission.class);

        // 获取类上的注解
        RequirePermission classAnnotation = AnnotationUtils.findAnnotation(method.getDeclaringClass(), RequirePermission.class);

        // 优先使用方法上的注解，如果没有则使用类上的注解
        Role[] requiredRoles = methodAnnotation != null ? methodAnnotation.value() :
                classAnnotation != null ? classAnnotation.value() : new Role[0];
        //TODO: 权限校验

        return joinPoint.proceed();
    }
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>部署到阿里云CDN的注意事项</title><link>https://blog.zhenxuna.cn/post/9/</link><guid isPermaLink="true">https://blog.zhenxuna.cn/post/9/</guid><pubDate>Mon, 30 Dec 2024 05:48:21 GMT</pubDate><content:encoded>&lt;h2&gt;起因&lt;/h2&gt;
&lt;p&gt;我一开始把博客部署在DCDN上，但是发现DCDN不会缓存图片/文件接口，于是我到了CDN。
CDN确实一开始用的很爽，都缓存了，但是，我发现，即使params改变了，阿里云居然返回的都是同样的json。
我一开始以为是前端的问题，还去把前端一些url路径从params转为了query，发现任然不济于事。
接着翻了翻阿里云CDN的文档，要设置Cache-Control: no-cache&lt;/p&gt;
&lt;h2&gt;解决&lt;/h2&gt;
&lt;p&gt;在Spring拦截器里给json接口添加上Cache-Control: no-cache就好了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@ControllerAdvice
public class GlobalResponseHeaderAdvice {

    @ModelAttribute
    public void setGlobalCacheControl(HttpServletRequest request, HttpServletResponse response) {
        String uri = request.getRequestURI();

        // 排除特定接口
        if (uri.startsWith(&quot;/api/file/download/&quot;)) {
            return;
        }

        // 默认添加 Cache-Control: no-cache
        response.setHeader(&quot;Cache-Control&quot;, &quot;no-cache&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>vite部署到nginx的一些常见问题</title><link>https://blog.zhenxuna.cn/post/8/</link><guid isPermaLink="true">https://blog.zhenxuna.cn/post/8/</guid><pubDate>Fri, 20 Dec 2024 05:07:47 GMT</pubDate><content:encoded>&lt;h2&gt;路由问题&lt;/h2&gt;
&lt;p&gt;vue router运行在hash模式下，会有/#/，觉得很丑，然后就运行在了history模式下，但是，这么模式下需要注意一些问题：
如果你没有在nginx里配置好的话，就会导致刷新页面404。
所以，需要在nginx里配置好&lt;code&gt;try_files&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;location / {
    try_files $uri $uri/ /index.html;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;api代理问题&lt;/h2&gt;
&lt;p&gt;相信大家在使用vite的过程中为了隐藏后端真实网址使用过vite的代理服务器吧，类似于这种&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;proxy: {
      &apos;/api&apos;: {
        target: &apos;http://localhost:port/&apos;,
        changeOrigin: true
      }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我当时认为，只要在vite里配置好代理就不需要操心了，我在main.js里配置了一些init方法，如果init报错就不创建App。其次，我也配置了上文提到的&lt;code&gt;try_files&lt;/code&gt;，所以这就导致了看不到任何的404报错，最后去除掉&lt;code&gt;try_files&lt;/code&gt;才定位到问题。
所以，即使在vite里配置好代理，也仅限于开发的情况下有效，在项目上线时仍然要在nginx里配置代理:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;location /api/ {
    proxy_pass http://localhost:port/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nginx会把/api/给删除，所以需要注意一下你的后端配置。
这下就可以正常访问后端接口了&lt;/p&gt;
</content:encoded></item><item><title>第十七周</title><link>https://blog.zhenxuna.cn/post/7/</link><guid isPermaLink="true">https://blog.zhenxuna.cn/post/7/</guid><description>第十七周开发内容</description><pubDate>Wed, 18 Dec 2024 09:17:06 GMT</pubDate><content:encoded>&lt;h2&gt;做了什么&lt;/h2&gt;
&lt;p&gt;最近心血来潮，想要开发一个属于自己的博客&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;个人博客的前端 &amp;amp; 后端 的基础开发，已经完成绝大多数功能&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;TODO&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;友链、工具 和 主页的前后端开发，以及一些细节样式 &amp;amp; 动画 的完善&lt;/li&gt;
&lt;li&gt;大学物理&lt;/li&gt;
&lt;li&gt;计组&lt;/li&gt;
&lt;li&gt;至于仿微信，，等有空再完善吧（狗头&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;TODO（长远 &lt;s&gt;下个学期？&lt;/s&gt;）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;学习MQ和servlet（虽然说Spring Boot很牛逼，但是基础还是得打好）&lt;/li&gt;
&lt;li&gt;学习jsp（下个学期的专业课之一）&lt;/li&gt;
&lt;li&gt;学习CSS 和 vite&lt;/li&gt;
&lt;li&gt;深入学习数据库&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>接入CloudFlare的人机验证接口</title><link>https://blog.zhenxuna.cn/post/5/</link><guid isPermaLink="true">https://blog.zhenxuna.cn/post/5/</guid><description>如何接入CloudFlare的人机验证接口</description><pubDate>Thu, 12 Dec 2024 12:37:41 GMT</pubDate><content:encoded>&lt;h2&gt;开始&lt;/h2&gt;
&lt;p&gt;先去 &lt;a href=&quot;https://www.cloudflare.com/zh-cn/application-services/products/turnstile/&quot;&gt;CloudFlare&lt;/a&gt; 注册一个账号并且开通拿到 Turnstile 的站点密钥和密钥&lt;/p&gt;
&lt;p&gt;前端以vue为例如何使用人机验证&lt;/p&gt;
&lt;p&gt;先导入CloudFlare提供的验证接口&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;onMounted(() =&amp;gt; {
  const script = document.createElement(&apos;script&apos;);
  script.src = &apos;https://challenges.cloudflare.com/turnstile/v0/api.js&apos;;
  script.async = true;
  script.onload = () =&amp;gt; {
    loadTurnstile();
  };
  document.head.appendChild(script);
});

function loadTurnstile() {
  turnstile.render(&apos;#example-container&apos;, {
    sitekey: &quot;&amp;lt;你拿到的站点密钥&amp;gt;&quot;,
    callback: function (token) {
      console.log(`Challenge Success ${token}`);
    },
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来在你的这个页面任何想验证的地方&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&quot;example-container&quot; data-sitekey=&quot;&amp;lt;你拿到的站点密钥&amp;gt;&quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完成以上操作后，你就可以在你的站点看到人机验证码(ps这个页面我随便写的没有调整样式)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/media/489f1551079312f4280d380d3c3f766d74e5f1301586a7e1e67451fb63f4b28e-1920.webp&quot; alt=&quot;image.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是这样还是不够的，需要后端配合二次验证&lt;/p&gt;
&lt;p&gt;后端需要对前端拿到的token进行验证，这里以java为例
如果你使用的是maven，需要导入依赖&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.apache.httpcomponents&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;httpcore&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;4.4.16&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.apache.httpcomponents&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;httpclient&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;4.5.14&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来创建一个工具类&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
/**
 * @author ATRI &amp;lt;miko217@xnu.edu.cn&amp;gt;
 */
public class TurnstileUtil {
    private static final String VERIFY_URL = &quot;https://challenges.cloudflare.com/turnstile/v0/siteverify&quot;;
    private static final String SECRET_KEY = &quot;&amp;lt;替换为你的密钥&amp;gt;&quot;;

    /**
     * 验证 Turnstile Token 是否有效
     *
     * @param token 用户提交的 Turnstile Token
     * @return 验证结果，true 表示验证通过，false 表示失败
     */
    public static boolean validateToken(String token) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(VERIFY_URL);

            JSONObject requestBody = new JSONObject();
            requestBody.put(&quot;secret&quot;, SECRET_KEY);
            requestBody.put(&quot;response&quot;, token);

            httpPost.setEntity(new StringEntity(requestBody.toString()));
            httpPost.setHeader(&quot;Content-type&quot;, &quot;application/json&quot;);

            CloseableHttpResponse response = httpClient.execute(httpPost);
            String responseString = EntityUtils.toString(response.getEntity());
            JSONObject jsonResponse = new JSONObject(responseString);

            return jsonResponse.getBoolean(&quot;success&quot;);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来需要在你任何想要认证的地方&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (!TurnstileUtil.validateToken(token)) { //这个token是前端经过验证后拿到的
     throw new ServiceException(ResponseType.TURNSTILE_VERIFICATION_FAILED);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来已经接入CloudFlare的接口了&lt;/p&gt;
</content:encoded></item><item><title>第十五周</title><link>https://blog.zhenxuna.cn/post/2/</link><guid isPermaLink="true">https://blog.zhenxuna.cn/post/2/</guid><description>第十五周开发内容</description><pubDate>Wed, 11 Dec 2024 11:39:47 GMT</pubDate><content:encoded>&lt;h1&gt;第十五周&lt;/h1&gt;
&lt;h2&gt;做了什么&lt;/h2&gt;
&lt;h3&gt;前端&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;修改密码&lt;/li&gt;
&lt;li&gt;修改个人信息&lt;/li&gt;
&lt;li&gt;初始化ws&lt;/li&gt;
&lt;li&gt;初始化本地数据库&lt;/li&gt;
&lt;li&gt;数据库常用操作封装（自己封装&lt;s&gt;MyBatis&lt;/s&gt;?）&lt;/li&gt;
&lt;li&gt;能够展示会话列表&lt;/li&gt;
&lt;li&gt;能够从本地数据库中拿到聊天记录&lt;/li&gt;
&lt;li&gt;发送文本消息&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;后端&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Redisson 的 RTpoic Pub/Sub&lt;/li&gt;
&lt;li&gt;所有的WS编写&lt;/li&gt;
&lt;li&gt;发送消息接口&lt;/li&gt;
&lt;li&gt;上传文件接口&lt;/li&gt;
&lt;li&gt;后端基本完结&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;TODO&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;!important：前端完成发送多媒体消息和美化展示文本消息&lt;/li&gt;
&lt;li&gt;!important：前端完成消息的实时加载&lt;/li&gt;
&lt;li&gt;&lt;s&gt;对CUD操作引入MQ&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;把对于大宗文件，上传到OSS中&lt;/li&gt;
&lt;li&gt;后端的一些TODO（大多都是insert or update)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item></channel></rss>