<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>大官人的博客</title>
        <link>https://www.daguanren.cc</link>
        <description>大官人的博客主要涉及：公众号和小程序的前沿知识，包括概念、产品设计、技术、实现、代码等全方面的解析；产品经理相关的互联网产品深度分析、原型设计技巧、工作职责、商业模式分析；运维相关API接口安全、Web攻击防护分析、MySQL、Linux系统使用介绍；常见的经济学概念分析、房价走势、高维牛股票技巧分析；办公软件Word、PowerPoint介绍。</description>
        <atom:link href="https://www.daguanren.cc/rss.html" rel="self" />
        <language>zh-cn</language>
        <lastBuildDate>Fri, 22 May 2026 20:37:43 GMT</lastBuildDate>
        <item>
            <title>什么是测试用例？</title>
            <link>https://www.daguanren.cc/post/ceshiyongli.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-2d5">✅ 一、什么是“测试用例”？（通俗理解）</a></li>
<li><a href="#toc-ab0">✅ 二、为什么要写测试用例？</a></li>
<li><a href="#toc-955">✅ 三、举个例子：thinkjs 的测试用例（API 示例）</a><ul>
<li><a href="#toc-08a">1. 目标代码（thinkjs 控制器）：</a></li>
<li><a href="#toc-35e">2. 测试用例（写法是 Mocha + think-testing）</a></li>
</ul>
</li>
<li><a href="#toc-27f">✅ 四、拆解一下测试用例的组成：</a></li>
<li><a href="#toc-e2e">✅ 总结</a><ul>
<li><a href="#toc-a11">✅ 简单回答：</a></li>
<li><a href="#toc-a21">对比：人工测试 vs 写测试用例</a></li>
<li><a href="#toc-b17">场景举例说明</a></li>
</ul>
</li>
<li><a href="#toc-3d2">✅ 场景一：上线前一天改了个登录逻辑</a></li>
<li><a href="#toc-e24">✅ 场景二：你和别人协作开发接口</a></li>
<li><a href="#toc-54e">✅ 场景三：有个 bug 难以复现</a><ul>
<li><a href="#toc-b65">最终建议</a></li>
</ul>
</li>
<li><a href="#toc-810">✔ 如果你是小项目、接口不多：</a></li>
<li><a href="#toc-306">✔ 如果你是团队协作、大项目、多人维护、持续迭代：</a></li>
</ul>
</div><p>测试用例就像做饭前的食谱，你告诉厨师：“用鸡蛋+番茄，炒出来要是红黄相间的番茄炒蛋才算成功”，这就是一个测试用例。</p>
<!--more-->

<h3><a id="toc-2d5" class="anchor" href="#toc-2d5"></a>✅ 一、什么是“测试用例”？（通俗理解）</h3>
<p>测试用例就是<strong>用来验证某个功能有没有做好</strong>的一组检查项，它包含：</p>
<ul>
<li>要测的功能（比如登录）</li>
<li>输入什么数据（比如用户名和密码）</li>
<li>预期输出（比如返回“登录成功”）</li>
<li>实际输出（测试时看到的结果）</li>
<li>判断测试是否通过</li>
</ul>
<hr>
<h3><a id="toc-ab0" class="anchor" href="#toc-ab0"></a>✅ 二、为什么要写测试用例？</h3>
<ol>
<li><strong>防止出错</strong>：写完功能后，有没有漏写逻辑？写错逻辑？测试用例能帮你查出来。</li>
<li><strong>防止回归问题</strong>：以后改代码，别人能快速判断有没有影响原有功能。</li>
<li><strong>可以自动化测试</strong>：用脚本执行一堆测试用例，让测试更快、更准。</li>
<li><strong>团队协作</strong>：前端、后端、测试、产品都能明确功能预期。</li>
</ol>
<hr>
<h3><a id="toc-955" class="anchor" href="#toc-955"></a>✅ 三、举个例子：thinkjs 的测试用例（API 示例）</h3>
<p>假设我们有一个用户登录接口 <code>/api/user/login</code>，POST 方法。</p>
<h4><a id="toc-08a" class="anchor" href="#toc-08a"></a>1. 目标代码（thinkjs 控制器）：</h4>
<pre><code class="language-js">// src/controller/api/user.js
module.exports = class extends think.Controller {
  async loginAction() {
    const username = this.post(&#39;username&#39;);
    const password = this.post(&#39;password&#39;);

    if (username === &#39;admin&#39; &amp;&amp; password === &#39;123456&#39;) {
      return this.success({ message: &#39;登录成功&#39; });
    } else {
      return this.fail(401, &#39;用户名或密码错误&#39;);
    }
  }
};</code></pre>
<hr>
<h4><a id="toc-35e" class="anchor" href="#toc-35e"></a>2. 测试用例（写法是 Mocha + think-testing）</h4>
<pre><code class="language-js">// test/controller/api/user.js
const test = require(&#39;think-testing&#39;);
const assert = require(&#39;assert&#39;);

describe(&#39;测试用户登录接口&#39;, () =&gt; {
  const instance = test();

  it(&#39;正确的用户名和密码应该登录成功&#39;, async () =&gt; {
    const res = await instance.post(&#39;/api/user/login&#39;).send({
      username: &#39;admin&#39;,
      password: &#39;123456&#39;
    });
    assert.equal(res.body.errno, 0); // 成功
    assert.equal(res.body.data.message, &#39;登录成功&#39;);
  });

  it(&#39;错误的密码应该返回错误&#39;, async () =&gt; {
    const res = await instance.post(&#39;/api/user/login&#39;).send({
      username: &#39;admin&#39;,
      password: &#39;wrong&#39;
    });
    assert.equal(res.body.errno, 401); // 错误码
    assert.equal(res.body.errmsg, &#39;用户名或密码错误&#39;);
  });
});</code></pre>
<hr>
<h3><a id="toc-27f" class="anchor" href="#toc-27f"></a>✅ 四、拆解一下测试用例的组成：</h3>
<table>
<thead>
<tr>
<th>用例编号</th>
<th>描述</th>
<th>输入</th>
<th>预期输出</th>
</tr>
</thead>
<tbody><tr>
<td>TC001</td>
<td>登录成功</td>
<td>username=admin<br>password=123456</td>
<td>errno=0, message=&quot;登录成功&quot;</td>
</tr>
<tr>
<td>TC002</td>
<td>密码错误</td>
<td>username=admin<br>password=wrong</td>
<td>errno=401, errmsg=&quot;用户名或密码错误&quot;</td>
</tr>
</tbody></table>
<hr>
<h3><a id="toc-e2e" class="anchor" href="#toc-e2e"></a>✅ 总结</h3>
<ul>
<li><strong>测试用例=说明书+验收标准</strong>。</li>
<li>有了测试用例，你就知道功能做完了没，有没有出错。</li>
<li>用 Mocha + think-testing 可以快速编写 thinkjs 的自动化测试用例。</li>
</ul>
<h2><a id="toc-a11" class="anchor" href="#toc-a11"></a>✅ 简单回答：</h2>
<p><strong>人工测当然可以，但自动化测试更稳、更快、更能应对复杂变化。</strong></p>
<p>就像你手动拧螺丝当然行，但你要组装 1 万台机器，用电动螺丝刀效率高得多。</p>
<hr>
<h2><a id="toc-a21" class="anchor" href="#toc-a21"></a>对比：人工测试 vs 写测试用例</h2>
<table>
<thead>
<tr>
<th>维度</th>
<th>人工测试（比如 Postman）</th>
<th>写测试用例（自动化）</th>
</tr>
</thead>
<tbody><tr>
<td>初始成本</td>
<td>✅ 低，上手快</td>
<td>❌ 有点麻烦，前期要写脚本</td>
</tr>
<tr>
<td>测试速度</td>
<td>❌ 慢，一个个点</td>
<td>✅ 一键运行几十上百条用例</td>
</tr>
<tr>
<td>可重复性</td>
<td>❌ 每次都要重复手动点</td>
<td>✅ 脚本写一次，随时跑，随时验证</td>
</tr>
<tr>
<td>回归测试</td>
<td>❌ 难，每次改功能都得手动再测</td>
<td>✅ 自动化用例直接复用，一键跑完</td>
</tr>
<tr>
<td>错误记录</td>
<td>❌ 人工易忘、出错难重现</td>
<td>✅ 自动比对预期和实际，留下日志</td>
</tr>
<tr>
<td>适合场景</td>
<td>一次性测试，功能不复杂</td>
<td>功能多、逻辑复杂、需要长期维护的项目</td>
</tr>
</tbody></table>
<hr>
<h2><a id="toc-b17" class="anchor" href="#toc-b17"></a>场景举例说明</h2>
<h3><a id="toc-3d2" class="anchor" href="#toc-3d2"></a>✅ 场景一：上线前一天改了个登录逻辑</h3>
<ul>
<li><strong>你用 Postman 测</strong>：你得一个一个接口点过去。</li>
<li><strong>你用测试用例</strong>：一条命令 <code>npm test</code>，几十个用例自动跑，10 秒内知道有没有被你改坏。</li>
</ul>
<hr>
<h3><a id="toc-e24" class="anchor" href="#toc-e24"></a>✅ 场景二：你和别人协作开发接口</h3>
<ul>
<li>没测试用例，你得靠人沟通“接口是不是正常”。</li>
<li>有测试用例，你说：“你改完跑下测试用例，绿灯就代表没出问题。”</li>
</ul>
<hr>
<h3><a id="toc-54e" class="anchor" href="#toc-54e"></a>✅ 场景三：有个 bug 难以复现</h3>
<ul>
<li>用 Postman 手动测时，漏掉了某种情况。</li>
<li>用测试脚本时，把所有输入组合都写进去，不容易漏掉。</li>
</ul>
<hr>
<h2><a id="toc-b65" class="anchor" href="#toc-b65"></a>最终建议</h2>
<h3><a id="toc-810" class="anchor" href="#toc-810"></a>✔ 如果你是小项目、接口不多：</h3>
<p> <strong>用 Postman 辅助测试也行</strong>，省事儿。</p>
<h3><a id="toc-306" class="anchor" href="#toc-306"></a>✔ 如果你是团队协作、大项目、多人维护、持续迭代：</h3>
<p> <strong>自动化测试用例一定要有！</strong>
哪怕只写核心接口，也能大大减轻你以后的维护压力。</p>
<hr>

            ]]></description>
            <pubDate>Fri, 08 Aug 2025 06:28:04 GMT</pubDate>
            <guid>https://www.daguanren.cc/post/ceshiyongli.html</guid>
        </item>
        <item>
            <title>qwen3用MacBook-intel版本的cpu推理遇到的问题和解决方式</title>
            <link>https://www.daguanren.cc/post/qwen3-yong-cpu-tui-li-yu-dao-de-wen-ti.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-109">1. 目标</a></li>
<li><a href="#toc-f88">2. 遇到的坑</a></li>
<li><a href="#toc-021">3. 解决思路与最终方案</a><ul>
<li><a href="#toc-05f">3.1 直接用 llama.cpp CLI（推荐）</a></li>
<li><a href="#toc-0ce">3.2 Python 方案（附加补丁）</a></li>
</ul>
</li>
<li><a href="#toc-f44">为什么会遇到这些问题？</a></li>
<li><a href="#toc-903">建议的配置与流程</a><ul>
<li><a href="#toc-5db">小结</a></li>
</ul>
</li>
</ul>
</div><p>阿里巴巴新出了千问3的模型，我想着用我的老电脑的cpu试一下，过程中遇到了一些问题，这里记录下。</p>
<!--more-->

<hr>
<h2><a id="toc-109" class="anchor" href="#toc-109"></a>1. 目标</h2>
<p>在一台 16 GB 内存的 MacBook 上，用纯 CPU 方式运行 <strong>Qwen3-4B-Instruct-2507</strong> 模型，完成中文对话式推理。</p>
<ul>
<li><strong>不依赖 GPU</strong>：全程在系统内存和多核 CPU 上执行。</li>
<li><strong>零冲突复现</strong>：提供一套简单、可复制的流程，避免 Python 依赖版本地狱。</li>
</ul>
<p>我的电脑配置如图：
<img alt="*.png" src="https://www.daguanren.cc/static/upload/20250807/upload_aaae1f1fac2add46cb0b3e9385359843.png" width="300" height=""></p>
<h2><a id="toc-f88" class="anchor" href="#toc-f88"></a>2. 遇到的坑</h2>
<ol>
<li><p><strong>Metal 后端初始化失败</strong>
<code>llama-cpp-python</code> 默认会加载 Metal 着色器库，报</p>
<pre><code class="hljs lang-delphi">ggml_metal_init: metal <span class="hljs-keyword">library</span> <span class="hljs-keyword">is</span> <span class="hljs-keyword">nil</span>  
ggml_metal_init: failed <span class="hljs-keyword">to</span> allocate context  </code></pre><p>导致无法运行。</p>
</li>
<li><p><strong>模型架构不识别</strong>
PyPI 上的旧版 <code>llama-cpp-python</code> 不支持最新的 <code>qwen3</code> 架构，加载 GGUF/模型时报</p>
<pre><code class="hljs lang-gams">unknown <span class="hljs-keyword">model</span> architecture: <span class="hljs-string">'qwen3'</span></code></pre></li>
<li><p><strong>Python 依赖冲突</strong></p>
<ul>
<li><p><strong>safetensors ↔ PyTorch</strong>：<code>safetensors</code> 内部硬编码了 <code>torch.uint16/uint32/uint64</code>，但 CPU-only PyTorch 只定义了 <code>torch.uint8</code>，导入立刻抛</p>
<pre><code class="hljs lang-coffeescript">AttributeError: <span class="hljs-built_in">module</span> <span class="hljs-string">'torch'</span> has <span class="hljs-literal">no</span> attribute <span class="hljs-string">'uintXX'</span></code></pre></li>
<li><p><strong>NumPy 版本不兼容</strong>：部分 C 扩展编译依赖于 NumPy 1.x，升级到 2.x 会引发崩溃。</p>
</li>
</ul>
</li>
<li><p><strong>源码编译痛点</strong></p>
<ul>
<li>克隆 <code>llama-cpp-python</code> 时子模块 <code>vendor/llama.cpp</code> 未带上，CMake 找不到底层库。</li>
<li>pip 直链 GitHub 多次因超时失败。</li>
</ul>
</li>
</ol>
<h2><a id="toc-021" class="anchor" href="#toc-021"></a>3. 解决思路与最终方案</h2>
<h3><a id="toc-05f" class="anchor" href="#toc-05f"></a>3.1 直接用 llama.cpp CLI（推荐）</h3>
<ol>
<li><p><strong>编译最新 llama.cpp</strong></p>
<pre><code class="language-bash">git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
cmake -S . -B build
cmake --build build --config Release --parallel $(sysctl -n hw.ncpu)</code></pre>
</li>
<li><p><strong>转换 Hugging Face safetensors → 单文件 GGUF</strong></p>
<pre><code class="language-bash">python convert_hf_to_gguf.py \
  --model ~/.cache/modelscope/hub/models/Qwen/Qwen3-4B-Instruct-2507 \
  --outfile ~/models/Qwen3-4B-Instruct-2507.gguf</code></pre>
</li>
<li><p><strong>纯 CPU 推理</strong></p>
<pre><code class="language-bash">cd build/bin
./llama-cli \
  --model ~/models/Qwen3-4B-Instruct-2507.gguf \
  --prompt &quot;请写一段古风小诗，题目是《秋思》&quot; \
  --threads 4 \
  --ctx-size 2048 \
  --n-predict 128 \
  --temp 0.7 \
  --top-k 40</code></pre>
</li>
</ol>
<p>这样完全绕开 Python 库冲突，支持 qwen3 架构，性能稳定。</p>
<h3><a id="toc-0ce" class="anchor" href="#toc-0ce"></a>3.2 Python 方案（附加补丁）</h3>
<p>如果仍想用 <code>transformers</code> + PyTorch，可在项目根目录下新建 <code>sitecustomize.py</code>，在 Python 启动时自动打补丁，避免 <code>safetensors</code> 报错：</p>
<pre><code class="language-python"># sitecustomize.py

try:
    import torch
    for name, alias in [
        (&quot;uint16&quot;, torch.int16),
        (&quot;uint32&quot;, torch.int32),
        (&quot;uint64&quot;, torch.int64),
    ]:
        if not hasattr(torch, name):
            setattr(torch, name, alias)
except ImportError:
    pass</code></pre>
<hr>
<h2><a id="toc-f44" class="anchor" href="#toc-f44"></a>为什么会遇到这些问题？</h2>
<ol>
<li><strong>模型太新</strong>：Qwen3 属于最前沿架构，旧版工具链没法识别，必须用最新代码或 <code>trust_remote_code</code>。</li>
<li><strong>半精度格式差异</strong>：配置文件中 <code>&quot;torch_dtype&quot;: &quot;bfloat16&quot;</code> 是 16 位格式；CPU 环境下常回退到 <code>float32</code>。</li>
<li><strong>safetensors ↔ PyTorch dtype 映射</strong>：<code>safetensors</code> 硬编码了多种无符号类型，但 CPU-only PyTorch 未定义，需补丁或合适版本。</li>
</ol>
<hr>
<p><strong>小结</strong></p>
<ul>
<li><strong>最稳妥</strong>：用最新 llama.cpp CLI，零 Python 冲突、零依赖。</li>
<li><strong>可选</strong>：Python 补丁配合环境导出，实现 Transformers 方案。</li>
<li><strong>环境导出</strong>：<code>conda env export</code> + <code>pip freeze</code>，让读者一键复现。</li>
</ul>
<p>成功效果图，推理巨慢，半小时才出结果：</p>
<img alt="*.png" src="https://www.daguanren.cc/static/upload/20250807/upload_3fc63545780532fe7babff509b10a957.png" width="800" height="">

<h2><a id="toc-903" class="anchor" href="#toc-903"></a>建议的配置与流程</h2>
<p>为什么这么慢呢？</p>
<p>在这台 2018 年款 MacBook Pro（16 GB DDR4-2400，6-核 Intel i7，Radeon Pro 555X + Intel UHD 630）上，用纯 CPU（PyTorch/transformers）来跑一个 4 B 参数级别的模型，实际体验大致是这样的：</p>
<ul>
<li><p><strong>FP32/FP16 原始模型（无量化、无特征加速）</strong></p>
<ul>
<li><p><strong>启动加载</strong>：模型权重十几 GB，几乎会把 16 GB RAM 吃满，常常卡在 “加载” 阶段并触发很长的磁盘→内存交换，可能需要 <strong>数分钟</strong> 甚至更久，或直接因 OOM 报错。</p>
</li>
<li><p><strong>推理速度</strong>：即便加载成功，也通常在 <strong>0.2–0.5 token/s</strong> 左右（5–10 秒／token）。</p>
<ul>
<li>生成 50 token ＝ 1–8 分钟</li>
<li>生成 200 token ＝ 20–80 分钟</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>量化＋高效推理（推荐）</strong></p>
<ul>
<li><p>使用 <strong>llama.cpp</strong>（或类似“ggml”库）把模型量化到 4 位或 8 位，开启 AVX/AVX2 优化：</p>
<ul>
<li><p><strong>q4_0（4 bit）</strong>：可以跑到 <strong>10–20 token/s</strong>。</p>
<ul>
<li>生成 100 token ＝ 5–10 秒</li>
</ul>
</li>
<li><p><strong>q8_0（8 bit）</strong>：可以跑到 <strong>5–10 token/s</strong>。</p>
<ul>
<li>生成 100 token ＝ 10–20 秒</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>优势</strong>：极大降低内存占用（2–4 GB），避免 OOM，且秒级响应。</p>
</li>
</ul>
</li>
<li><p><strong>为何不能利用你的 Radeon Pro 555X 或 UHD 630 加速？</strong></p>
<ul>
<li>Apple MPS 后端仅支持 Apple Silicon（M1/M2 系列）。Intel + Radeon GPU 在 PyTorch/transformers 上没有通用加速方案，只能靠 CPU。</li>
</ul>
</li>
</ul>
<hr>
<ol>
<li><p><strong>准备量化模型</strong></p>
<pre><code class="language-bash"># 例如用 llama.cpp 工具链把 HF checkpoint 转成 q4_0
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
./convert.py path/to/model.bin --outfile model-q4_0.bin --quantize q4_0</code></pre>
</li>
<li><p><strong>安装并运行 llama.cpp</strong></p>
<pre><code class="language-bash">make
./main -m model-q4_0.bin -p &quot;你好，模型！&quot; -t 6</code></pre>
<ul>
<li><code>-t 6</code> 指定 6 线程，你也可以根据 <code>htop</code> 监控，尝试 <code>4–8</code> 线程的平衡点。</li>
</ul>
</li>
<li><p><strong>体验速度</strong></p>
<ul>
<li>量化后常可以看到 <strong>10–20 token/s</strong>，生成一个短答（50 字）只要 <strong>不到 1 秒</strong>。</li>
</ul>
</li>
</ol>
<hr>
<h4><a id="toc-5db" class="anchor" href="#toc-5db"></a>小结</h4>
<ul>
<li><strong>纯 CPU 非量化</strong>：几秒到几分钟／token，根本达不到交互速度。</li>
<li><strong>量化 + llama.cpp</strong>：秒级响应，且稳定跑在你这台 16 GB MacBook Pro 上。</li>
</ul>
<p>如果你想在本地玩转 4 B、7 B 这样的模型，强烈推荐走量化＋ llama.cpp（或 ONNX Runtime＋OpenVINO、CTranslate2）这条路。这样就不用再“等半天”了。</p>
<p>导出文件：</p>
<pre><code class="language-yaml"># qwen3-instruct-environment.yml
name: qwen3-instruct
channels:
  - pytorch
  - defaults
dependencies:
  - aom=3.6.0
  - blas=1.0
  - brotlicffi=1.0.9.2
  - bzip2=1.0.8
  - ca-certificates=2025.2.25
  - cairo=1.16.0
  - certifi=2025.7.14
  - cffi=1.17.1
  - charset-normalizer=3.3.2
  - cpuonly=2.0
  - dav1d=1.2.1
  - expat=2.7.1
  - filelock=3.17.0
  - fontconfig=2.14.1
  - freetype=2.13.3
  - fribidi=1.0.10
  - gettext=0.21.0
  - gmp=6.3.0
  - gmpy2=2.2.1
  - graphite2=1.3.14
  - harfbuzz=10.2.0
  - icu=73.1
  - idna=3.7
  - intel-openmp=2023.1.0
  - jinja2=3.1.6
  - jpeg=9e
  - lcms2=2.16
  - lerc=4.0.0
  - libavif=1.1.1
  - libcxx=17.0.6
  - libdeflate=1.22
  - libffi=3.4.4
  - libglib=2.84.2
  - libiconv=1.16
  - libjpeg-turbo=2.0.0
  - libpng=1.6.39
  - libtiff=4.7.0
  - libwebp-base=1.3.2
  - libxml2=2.13.8
  - llvm-openmp=17.0.6
  - lz4-c=1.9.4
  - markupsafe=3.0.2
  - mkl=2023.1.0
  - mkl-service=2.4.0
  - mkl_fft=1.3.8
  - mkl_random=1.2.4
  - mpc=1.3.1
  - mpfr=4.2.1
  - mpmath=1.3.0
  - ncurses=6.5
  - networkx=3.4.2
  - numpy=1.24.3
  - numpy-base=1.24.3
  - openjpeg=2.5.2
  - openssl=3.0.17
  - pcre2=10.42
  - pillow=11.3.0
  - pip=25.1
  - pixman=0.40.0
  - pycparser=2.21
  - pysocks=1.7.1
  - python=3.10.18
  - pytorch=2.2.2
  - pytorch-mutex=1.0
  - pyyaml=6.0.2
  - readline=8.3
  - requests=2.32.4
  - setuptools=78.1.1
  - sqlite=3.50.2
  - sympy=1.13.3
  - tbb=2021.8.0
  - tk=8.6.14
  - torchaudio=2.2.2
  - torchvision=0.17.2
  - typing_extensions=4.12.2
  - tzdata=2025b
  - urllib3=2.5.0
  - wheel=0.45.1
  - xz=5.6.4
  - yaml=0.2.5
  - zlib=1.2.13
  - zstd=1.5.6
  - pip:
      - accelerate==1.9.0
      - fsspec==2025.7.0
      - hf-xet==1.1.7
      - huggingface-hub==0.34.3
      - packaging==25.0
      - psutil==7.0.0
      - regex==2025.7.34
      - safetensors==0.6.1
      - tokenizers==0.21.4
      - tqdm==4.67.1
      - transformers==4.55.0
prefix: /opt/miniconda3/envs/qwen3-instruct
</code></pre>
<p>requirements.txt</p>
<pre><code class="hljs lang-elixir">accelerate==<span class="hljs-number">1.9</span>.0
brotlicffi @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/sy/f</span>16zz6x50xz3113nwtb9bvq00000gp/T/abs_05qpoetyrv/croot/brotlicffi_1736182869800/work
certifi @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/c_/qfmhj</span>66j0tn016nkx_th4hxm0000gp/T/abs_fckkf4c22q/croot/certifi_1752653720654/work/certifi
cffi @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/c_/qfmhj</span>66j0tn016nkx_th4hxm0000gp/T/abs_51d1gdg4kr/croot/cffi_1736183297412/work
charset-normalizer @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//croot</span><span class="hljs-regexp">/charset-normalizer_1721748349566/work</span>
filelock @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/sy/f</span>16zz6x50xz3113nwtb9bvq00000gp/T/abs_90zocybn3d/croot/filelock_1744281394121/work
fsspec==<span class="hljs-number">2025.7</span>.0
gmpy2 @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/sy/f</span>16zz6x50xz3113nwtb9bvq00000gp/T/abs_a9jzez1qcu/croot/gmpy2_1738085473561/work
hf-xet==<span class="hljs-number">1.1</span>.<span class="hljs-number">7</span>
huggingface-hub==0.<span class="hljs-number">34.3</span>
idna @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/c_/qfmhj</span>66j0tn016nkx_th4hxm0000gp/T/abs_2b_jn555_n/croot/idna_1714398852258/work
Jinja2 @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/c_/qfmhj</span>66j0tn016nkx_th4hxm0000gp/T/abs_f4xtf61_ib/croot/jinja2_1741715270435/work
MarkupSafe @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/sy/f</span>16zz6x50xz3113nwtb9bvq00000gp/T/abs_76vhch455b/croot/markupsafe_1738584048225/work
mkl-fft @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/c_/qfmhj</span>66j0tn016nkx_th4hxm0000gp/T/abs_af8wl3mjfu/croot/mkl_fft_1695058185839/work
mkl-random @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/c_/qfmhj</span>66j0tn016nkx_th4hxm0000gp/T/abs_0fq2s894lk/croot/mkl_random_1695059822529/work
mkl-service==<span class="hljs-number">2.4</span>.0
mpmath @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/c_/qfmhj</span>66j0tn016nkx_th4hxm0000gp/T/abs_8fyoqwupl2/croot/mpmath_1690848275746/work
networkx @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/sy/f</span>16zz6x50xz3113nwtb9bvq00000gp/T/abs_ba1do7fwxc/croot/networkx_1737043398715/work
numpy @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//</span>Users/ec2-user/mkl/numpy_and_numpy_base_1682973769567/work
packaging==<span class="hljs-number">25.0</span>
pillow @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/c_/qfmhj</span>66j0tn016nkx_th4hxm0000gp/T/abs_59qe1phg3h/croot/pillow_1752524533185/work
psutil==<span class="hljs-number">7.0</span>.0
pycparser @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//tmp</span><span class="hljs-regexp">/build/</span><span class="hljs-number">80754</span>af9/pycparser_1636541352034/work
PySocks @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//</span>Users/builder/ci_310/pysocks_1642536366386/work
PyYAML @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/sy/f</span>16zz6x50xz3113nwtb9bvq00000gp/T/abs_34t43scp15/croot/pyyaml_1728658999838/work
regex==<span class="hljs-number">2025.7</span>.<span class="hljs-number">34</span>
requests @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/c_/qfmhj</span>66j0tn016nkx_th4hxm0000gp/T/abs_3c5r_7_lxu/croot/requests_1750426340266/work
safetensors==0.<span class="hljs-number">6.1</span>
sympy @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/c_/qfmhj</span>66j0tn016nkx_th4hxm0000gp/T/abs_3ej0z2jk7t/croot/sympy_1738108502551/work
tokenizers==0.<span class="hljs-number">21.4</span>
torch==<span class="hljs-number">2.2</span>.<span class="hljs-number">2</span>
torchaudio==<span class="hljs-number">2.2</span>.<span class="hljs-number">2</span>
torchvision==0.<span class="hljs-number">17.2</span>
tqdm==<span class="hljs-number">4.67</span>.<span class="hljs-number">1</span>
transformers==<span class="hljs-number">4.55</span>.0
typing_extensions @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/sy/f</span>16zz6x50xz3113nwtb9bvq00000gp/T/abs_6dn2s8ln8g/croot/typing_extensions_1734714858107/work
urllib3 @ <span class="hljs-symbol">file:</span>/<span class="hljs-regexp">//private</span><span class="hljs-regexp">/var/folders</span><span class="hljs-regexp">/c_/qfmhj</span>66j0tn016nkx_th4hxm0000gp/T/abs_f8xnjmnanx/croot/urllib3_1750775879857/work
</code></pre><p>导出命令：</p>
<pre><code class="language-bash"># 一键导出当前 Conda 环境和 pip 包列表，方便读者一键复现
conda activate qwen3-instruct
conda env export --no-builds --name qwen3-instruct &gt; qwen3-instruct-environment.yml
pip freeze &gt; requirements.txt</code></pre>

            ]]></description>
            <pubDate>Thu, 07 Aug 2025 03:43:32 GMT</pubDate>
            <guid>https://www.daguanren.cc/post/qwen3-yong-cpu-tui-li-yu-dao-de-wen-ti.html</guid>
        </item>
        <item>
            <title>解决谷歌浏览器http自动跳转https的问题</title>
            <link>https://www.daguanren.cc/post/jie-jue-gu-ge-liu-lan-qi-http-zi-dong-tiao-zhuan-https-de-wen-ti.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-800">1. 删除域名安全策略</a></li>
<li><a href="#toc-ab3">2. 清除缓存（非常重要）</a></li>
<li><a href="#toc-740">3. 调整安全提醒（可选）</a></li>
</ul>
</div><p>在日常浏览网页的过程中，可能会遇到谷歌浏览器自动将HTTP链接跳转到HTTPS的问题。以下步骤可以帮助你解决这一问题。</p>
<!--more-->

<h4><a id="toc-800" class="anchor" href="#toc-800"></a>1. 删除域名安全策略</h4>
<p>首先，打开谷歌浏览器HSTS设置页面：</p>
<p>在谷歌浏览器的地址栏输入：</p>
<pre><code class="hljs lang-avrasm"><span class="hljs-symbol">chrome:</span>//net-internals/<span class="hljs-meta">#hsts</span></code></pre><p>然后在<code>Delete domain security policies</code>一侧输入需要处理的域名，并点击<code>Delete</code>按钮。</p>
<img alt="*.png" src="https://www.daguanren.cc/static/upload/20240620/upload_b492a7145f66dae272890eed0a23fead.png" width="600" height="">


<h4><a id="toc-ab3" class="anchor" href="#toc-ab3"></a>2. 清除缓存（非常重要）</h4>
<p>清除浏览器缓存是确保更改生效的关键步骤。你可以通过以下链接直接打开清除缓存的页面：</p>
<pre><code class="hljs lang-dts"><span class="hljs-symbol">chrome:</span><span class="hljs-comment">//settings/clearBrowserData</span></code></pre><img alt="*.png" src="https://www.daguanren.cc/static/upload/20240620/upload_35c58a4315803661b036e35e77d3f88d.png" width="700" height="">

<h4><a id="toc-740" class="anchor" href="#toc-740"></a>3. 调整安全提醒（可选）</h4>
<p>如果你需要调整安全提醒设置，可以通过以下链接打开安全设置页面：</p>
<pre><code class="hljs lang-dts"><span class="hljs-symbol">chrome:</span><span class="hljs-comment">//settings/security</span></code></pre><img alt="*.png" src="https://www.daguanren.cc/static/upload/20240620/upload_1d4996e439b9fb94888ac68c7c9c4648.png" width="700" height="">

<p>完成上述步骤后，谷歌浏览器将不再自动将HTTP链接跳转到HTTPS。</p>

            ]]></description>
            <pubDate>Thu, 20 Jun 2024 05:41:36 GMT</pubDate>
            <guid>https://www.daguanren.cc/post/jie-jue-gu-ge-liu-lan-qi-http-zi-dong-tiao-zhuan-https-de-wen-ti.html</guid>
        </item>
        <item>
            <title>如何离线环境安装docker（以CentOS 7.7 64位为例）</title>
            <link>https://www.daguanren.cc/post/ru-he-li-xian-huan-jing-an-zhuang-docker（-yi-CentOS 7.7 64-wei-wei-li-）.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-1a5">添加Docker CE 仓库</a></li>
<li><a href="#toc-8c3">离线下载Docker CE和依赖包</a></li>
</ul>
</div><p>先找台能联网的相同系统型号（CentOS 7.7 64位）机器将所需要的安装包下载下来。</p>
<!--more-->

<p>需要确保已经将Docker的官方仓库添加到了你的yum仓库列表中。这是离线下载<code>docker-ce</code>软件包之前的一个重要步骤。下面是如何添加Docker仓库并下载<code>docker-ce</code>包的步骤：</p>
<h3><a id="toc-1a5" class="anchor" href="#toc-1a5"></a>添加Docker CE 仓库</h3>
<ol>
<li><p><strong>安装所需的包</strong>：首先，确保你的系统已经安装了<code>yum-utils</code>，这个包包含<code>yum-config-manager</code>，用于管理你的yum仓库。</p>
<pre><code class="hljs lang-cmake">sudo yum <span class="hljs-keyword">install</span> -y yum-utils</code></pre></li>
<li><p><strong>添加Docker仓库</strong>：使用以下命令添加Docker的官方yum仓库。</p>
<pre><code class="hljs lang-vim">sudo yum-config-manager --<span class="hljs-built_in">add</span>-repo http<span class="hljs-variable">s:</span>//download.docker.<span class="hljs-keyword">com</span>/linux/centos/docker-<span class="hljs-keyword">ce</span>.repo</code></pre></li>
</ol>
<h3><a id="toc-8c3" class="anchor" href="#toc-8c3"></a>离线下载Docker CE和依赖包</h3>
<ol>
<li><p><strong>下载Docker CE及其依赖</strong>：现在，你已经添加了Docker仓库，可以尝试再次下载<code>docker-ce</code>软件包及其依赖。这里的--downloaddir=./docker-ce-packages，是下载到当前文件夹的docker-ce-packages文件下:</p>
<pre><code class="hljs lang-jboss-cli">sudo yum install <span class="hljs-params">--downloadonly</span> <span class="hljs-params">--downloaddir=</span><span class="hljs-string">./docker-ce-packages</span> docker-ce</code></pre><p>注意：确保你正在执行这些命令的机器是联网的，并且能够访问<code>download.docker.com</code>。</p>
</li>
</ol>
<p>如果仍然无法找到<code>docker-ce</code>软件包，可能是因为Docker官方仓库的URL发生了变化或者是你的系统版本与Docker仓库不兼容。在这种情况下，建议访问Docker的官方文档或者社区论坛来获取最新的安装指导和帮助。</p>
<p>大概下载完就是以下这样子：</p>
<p><img src="https://www.daguanren.cc/static/upload/20240411/upload_296a4ec52d51cec5470b362602a6fed7.png" alt="image.png"></p>
<ol start="2">
<li><strong>用一键脚本安装docker</strong>：</li>
</ol>
<p>以下是一个简单的一键安装脚本示例，用于在目标机器上离线安装Docker。这个脚本包括了您之前提到的所有步骤：</p>
<pre><code class="language-bash">#!/bin/bash

# 替换为包含Docker安装包的目录的路径
DOWNLOAD_DIR=&quot;&lt;下载目录&gt;&quot;

# 转到包含下载包的目录
cd $DOWNLOAD_DIR

# 安装Docker软件包及其依赖
sudo yum install -y ./*.rpm

# 启动Docker服务
sudo systemctl start docker

# 设置Docker开机启动
sudo systemctl enable docker

# 验证Docker安装
docker version

# 输出完成消息
echo &quot;Docker安装完成！&quot;</code></pre>
<p>将这段脚本保存到一个文件中，比如命名为<code>install_docker.sh</code>，然后在目标机器上执行这个脚本。在运行脚本之前，请确保替换<code>&lt;下载目录&gt;</code>为实际包含Docker安装包的目录的路径，并确保该脚本具有执行权限：</p>
<pre><code class="language-bash">chmod +x install_docker.sh</code></pre>
<p>然后，你可以通过下面的命令运行这个脚本：</p>
<pre><code class="language-bash">./install_docker.sh</code></pre>
<p>请确保在复制包和安装之前检查所有文件的完整性，以避免潜在的安全风险。</p>
<p>成功安装：</p>
<p><img src="https://www.daguanren.cc/static/upload/20240411/upload_3f33a8e81f46e4772849958caa0ab591.png" alt="image.png"></p>

            ]]></description>
            <pubDate>Thu, 11 Apr 2024 02:17:18 GMT</pubDate>
            <guid>https://www.daguanren.cc/post/ru-he-li-xian-huan-jing-an-zhuang-docker（-yi-CentOS 7.7 64-wei-wei-li-）.html</guid>
        </item>
        <item>
            <title>通用接口对接的加解密方式</title>
            <link>https://www.daguanren.cc/post/tong-yong-jie-kou-dui-jie-de-jia-jie-mi-fang-shi.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-7a1">安全API接口文档</a><ul>
<li><a href="#toc-ddb">安全机制概览</a></li>
<li><a href="#toc-90a">数据加密</a><ul>
<li><a href="#toc-8f6">加密算法</a></li>
<li><a href="#toc-18f">加密步骤</a></li>
<li><a href="#toc-0b3">请求格式</a></li>
</ul>
</li>
<li><a href="#toc-695">请求签名</a><ul>
<li><a href="#toc-a2e">签名生成</a></li>
<li><a href="#toc-8e3">签名传输</a></li>
</ul>
</li>
<li><a href="#toc-69c">服务器端验证</a></li>
<li><a href="#toc-a25">安全建议</a></li>
</ul>
</li>
<li><a href="#toc-715">带时间戳的简化版安全API接口文档</a><ul>
<li><a href="#toc-90a">数据加密</a><ul>
<li><a href="#toc-8f6">加密算法</a></li>
<li><a href="#toc-db4">加密流程</a></li>
<li><a href="#toc-0b3">请求格式</a></li>
</ul>
</li>
<li><a href="#toc-b73">服务器端解密与验证</a></li>
<li><a href="#toc-4c0">安全注意事项</a></li>
</ul>
</li>
</ul>
</div><h1><a id="toc-7a1" class="anchor" href="#toc-7a1"></a>安全API接口文档</h1>
<p>本文档提供了关于如何安全地调用API的指南，包括数据的加密传输和请求的签名验证。</p>
<!--more-->

<h2><a id="toc-ddb" class="anchor" href="#toc-ddb"></a>安全机制概览</h2>
<p>为确保数据传输的安全性和验证请求的合法性，我们实施了以下安全机制：</p>
<ol>
<li><strong>数据加密</strong>：使用AES-256-CBC算法对请求体数据进行加密，确保传输数据的保密性。</li>
<li><strong>请求签名</strong>：通过SHA-256哈希算法对请求参数进行签名，验证请求的完整性和认证性。</li>
</ol>
<h2><a id="toc-90a" class="anchor" href="#toc-90a"></a>数据加密</h2>
<h3><a id="toc-8f6" class="anchor" href="#toc-8f6"></a>加密算法</h3>
<ul>
<li><strong>算法</strong>：AES-256-CBC</li>
<li><strong>密钥长度</strong>：256位</li>
<li><strong>初始向量（IV）</strong>：随机生成，每次请求都应不同</li>
</ul>
<h3><a id="toc-18f" class="anchor" href="#toc-18f"></a>加密步骤</h3>
<ol>
<li><strong>准备数据</strong>：将请求体数据转换为JSON字符串格式。</li>
<li><strong>生成IV</strong>：随机生成16字节的初始向量（IV）。</li>
<li><strong>加密数据</strong>：使用AES-256-CBC算法，预共享的密钥，和随机生成的IV对数据进行加密。</li>
<li><strong>拼接IV和密文</strong>：将IV和加密后的数据拼接，然后进行Base64编码，得到最终的请求体数据。</li>
</ol>
<h3><a id="toc-0b3" class="anchor" href="#toc-0b3"></a>请求格式</h3>
<p>加密后的请求体应该包含Base64编码的拼接IV和密文，如下所示：</p>
<pre><code class="language-json">{
  &quot;encryptedData&quot;: &quot;Base64编码的IV+加密数据&quot;
}</code></pre>
<h2><a id="toc-695" class="anchor" href="#toc-695"></a>请求签名</h2>
<h3><a id="toc-a2e" class="anchor" href="#toc-a2e"></a>签名生成</h3>
<ol>
<li><strong>收集参数</strong>：包括所有请求参数和加密的请求体。</li>
<li><strong>排序和格式化</strong>：将所有参数按照键名字典序排序，然后格式化为<code>key=value</code>形式，并用<code>&amp;</code>连接。</li>
<li><strong>生成签名</strong>：使用SHA-256算法对上一步得到的字符串进行哈希运算，将哈希值作为签名。</li>
</ol>
<h3><a id="toc-8e3" class="anchor" href="#toc-8e3"></a>签名传输</h3>
<p>将生成的签名放在HTTP请求头中发送，例如：</p>
<pre><code class="hljs lang-xml">X-Signature: <span class="hljs-tag">&lt;<span class="hljs-name">签名值</span>&gt;</span></code></pre><h2><a id="toc-69c" class="anchor" href="#toc-69c"></a>服务器端验证</h2>
<ol>
<li><strong>验证签名</strong>：服务器使用相同的规则重新生成签名，并与请求头中的签名进行比较。</li>
<li><strong>解密数据</strong>：验证签名通过后，使用相同的AES密钥和IV解密请求体中的加密数据。</li>
</ol>
<h2><a id="toc-a25" class="anchor" href="#toc-a25"></a>安全建议</h2>
<ul>
<li>密钥应安全共享和存储，避免通过不安全的渠道传递。</li>
<li>使用HTTPS协议传输所有请求，保护数据在传输过程中的安全。</li>
<li>定期更新密钥，增强系统的安全性。</li>
</ul>
<hr>
<p>请根据实际的业务需求和安全要求调整以上内容。务必确保所有的安全措施都经过充分测试，并且符合相关的安全标准和最佳实践。</p>
<p>为了增强简化版加密方案的安全性，可以在请求中加入时间戳。时间戳有助于防止重放攻击（replay attacks），即攻击者捕获并重新发送之前的请求。在请求中包含时间戳后，服务器可以检查时间戳以确保请求是在合理的时间窗口内发送的。下面是包含时间戳的简化版加密方案的接口文档：</p>
<hr>
<h1><a id="toc-715" class="anchor" href="#toc-715"></a>带时间戳的简化版安全API接口文档</h1>
<p>本文档描述了如何使用简化的AES加密方案和时间戳安全地调用API，以确保数据传输的安全性并防止重放攻击。</p>
<h2><a id="toc-90a" class="anchor" href="#toc-90a"></a>数据加密</h2>
<h3><a id="toc-8f6" class="anchor" href="#toc-8f6"></a>加密算法</h3>
<ul>
<li><strong>算法</strong>：AES-256</li>
<li><strong>模式</strong>：ECB（电子密码本模式）</li>
<li><strong>填充方式</strong>：PKCS#7</li>
</ul>
<h3><a id="toc-db4" class="anchor" href="#toc-db4"></a>加密流程</h3>
<ol>
<li><strong>数据准备</strong>：将需要传输的数据（如JSON格式的请求体）和当前的时间戳（如UNIX时间戳）一同组织成字符串格式。</li>
<li><strong>执行加密</strong>：使用预共享的AES-256密钥对包含数据和时间戳的字符串进行加密。在ECB模式下，不使用IV。</li>
<li><strong>编码</strong>：将加密后的数据进行Base64编码，以便在HTTP请求中安全传输。</li>
</ol>
<h3><a id="toc-0b3" class="anchor" href="#toc-0b3"></a>请求格式</h3>
<p>请求体应包含经过Base64编码的加密数据，示例如下：</p>
<pre><code class="language-json">{
  &quot;encryptedData&quot;: &quot;Base64编码的加密数据&quot;
}</code></pre>
<h2><a id="toc-b73" class="anchor" href="#toc-b73"></a>服务器端解密与验证</h2>
<ol>
<li><strong>解密数据</strong>：服务器收到请求后，将Base64编码的加密数据解码，并使用相同的AES密钥进行解密，以获取包含原始数据和时间戳的字符串。</li>
<li><strong>验证时间戳</strong>：解密得到的字符串中包含时间戳信息，服务器需要验证时间戳以确保请求是在预定的时间窗口内（如5分钟内）发送的。如果请求超出时间窗口，则可能是重放攻击，应拒绝该请求。</li>
</ol>
<h2><a id="toc-4c0" class="anchor" href="#toc-4c0"></a>安全注意事项</h2>
<ul>
<li><strong>密钥安全</strong>：确保加密密钥的安全性和保密性，避免通过不安全的渠道共享密钥。</li>
<li><strong>时间同步</strong>：客户端和服务器需要时间同步，以确保时间戳验证的准确性。</li>
<li><strong>通信安全</strong>：建议通过HTTPS等安全协议传输所有请求，防止数据在传输过程中被截获和篡改。</li>
<li><strong>密钥管理</strong>：定期更新加密密钥，以增强安全性。</li>
</ul>
<hr>
<p>此接口文档提供了一个使用时间戳和简化AES加密的安全通信方案。尽管这种方案相对简单，但加入时间戳增加了一定的安全性，有助于防止重放攻击。在实施此方案时，请确保充分考虑所有安全因素，确保它满足您的安全需求。</p>

            ]]></description>
            <pubDate>Sun, 18 Feb 2024 06:39:34 GMT</pubDate>
            <guid>https://www.daguanren.cc/post/tong-yong-jie-kou-dui-jie-de-jia-jie-mi-fang-shi.html</guid>
        </item>
        <item>
            <title>京东快递接口对接</title>
            <link>https://www.daguanren.cc/post/jing-dong-kuai-di-jie-kou-dui-jie.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-1b2">第一步：确认应用类别</a></li>
<li><a href="#toc-af1">第二步：获取access_token</a></li>
<li><a href="#toc-793">第三步：组织url及参数</a></li>
<li><a href="#toc-174">第四步：调用所需的接口</a></li>
<li><a href="#toc-1bb">注意事项</a></li>
</ul>
</div><p>得益于京东自营商城，京东建立了庞大的物流体系，京东快递也对外开通了服务。我们的程序中需要用到京东快递进行发货，场景是我们做基因检测业务，根据顾客下单的地址，给顾客寄送采样盒，客户就像做核酸一样，采样后将样本放置到采样试管里给我们寄回。我们收到采样试管送入实验室进行实验。顾客将采样试管寄回的地址是固定的，运费也是我们自定义的。所以需要跟快递公司进行接口对接。本文将记录和讲述对于京东快递的对接方式和步骤。</p>
<!--more-->

<h3><a id="toc-1b2" class="anchor" href="#toc-1b2"></a>第一步：确认应用类别</h3>
<p>我们的程序里需要和京东快递对接，调用京东快递的接口实现产品的发货。文档参考：
<a href="https://cloud.jdl.com/#/open-business-document/access-guide/267/53375">京东物流-开放平台</a></p>
<p>顾客属于C端，我们属于商家B端，所以这种顾客寄回采样盒给到我们公司的对应京东快递文档中的C2B模式：</p>
<pre><code class="hljs lang-undefined">支持逆向物流，C端客户寄件给商家，例如：图书回收、洗护业务、电商平台客户退货发起的逆向业务等业务场景，以下的文档和API描述简称快递C2B</code></pre><p>应用类别属于：商家自研应用</p>
<p><img src="https://www.daguanren.cc/static/upload/20230827/upload_de4e078dc714d13984cae252fc20bc77.png" alt="image.png"></p>
<p>对应的时序图如下：</p>
<p><img src="https://www.daguanren.cc/static/upload/20230827/upload_d09dc8f6f2f668aa8d100470efb59fb3.png" alt="41768117d8e4d61d.png"></p>
<h3><a id="toc-af1" class="anchor" href="#toc-af1"></a>第二步：获取access_token</h3>
<p>调用以上接口同样和微信的接口类似，都需要用到access_token，不同于微信的
access_token一般只有2个小时的有效期，京东快递的access_token一旦获取了，会有1年的有效期。但要注意的是，京东快递的access_token分为预发环境和生产环境的。获取的方式可以参考如下文档：<a href="https://cloud.jdl.com/#/devSupport/53392">授权介绍</a>，基本还是比较简单的，只需要用到AppKey就可以获取。</p>
<p>或者在京东提供的一个专门调试api的网页：<a href="https://cloud.jdl.com/#/open-business-document/common-development-tools/267/api-test">常用开发工具</a> 中点击获取“获取AccessToken ”来按提示获取也可以，如下图所示：</p>
<p><img src="https://www.daguanren.cc/static/upload/20230827/upload_82eb1d86630bf6a6ec409c1a85b3f3d0.png" alt="WechatIMG4773.jpg"></p>
<h3><a id="toc-793" class="anchor" href="#toc-793"></a>第三步：组织url及参数</h3>
<p>获取到access_token后，组织url及参数，生成类似这种格式的请求头：</p>
<pre><code class="hljs lang-sas">https://api.jdl.com/ecap/v1/orders/precheck?app_key=yourAppKey<span class="hljs-variable">&amp;access_token</span>=yourAccessToken<span class="hljs-variable">&amp;timestamp</span>=2022-04-01 23:04:14<span class="hljs-variable">&amp;v</span>=2.0<span class="hljs-variable">&amp;LOP</span>-DN=ECAP<span class="hljs-variable">&amp;sign</span>=yourSign</code></pre><p>这里面的计算签名和微信各类文档中的其实是大同小异的。</p>
<p>参考<a href="https://cloud.jdl.com/#/open-business-document/access-guide/267/53211">非SDK调用API（开发者→京东物流）</a></p>
<p>文档中有Java和Python示例代码，如果不能满足你的需求，可以复制出来到chatgpt让其帮你改写成想要的语言，比如Nodejs：</p>
<pre><code class="hljs lang-typescript"><span class="hljs-comment">/**
     * 根据京东快递的规则，将输入的url增加token和加密串，返回生成好的URL
     * @param {type}  
     * @param {type} 
     * @return 
     */</span>
    <span class="hljs-keyword">async</span> generateURL(path, algorithm, body) {
        <span class="hljs-keyword">const</span> base_uri = <span class="hljs-string">'YOUR_BASE_URI'</span>;
        <span class="hljs-keyword">const</span> app_key = <span class="hljs-string">'YOUR_APPKEY'</span>;
        <span class="hljs-keyword">const</span> app_secret = <span class="hljs-string">'YOUR_APPSECRET'</span>;
        <span class="hljs-keyword">const</span> access_token = <span class="hljs-string">'YOUR_ACCESS_TOKEN'</span>;
        <span class="hljs-keyword">const</span> domain = <span class="hljs-string">'ECAP'</span>;
        <span class="hljs-keyword">const</span> method = <span class="hljs-string">'POST'</span>;
        <span class="hljs-keyword">const</span> timestamp = moment(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTime()).format(<span class="hljs-string">'YYYY-MM-DD HH:mm:ss'</span>);

        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span>(body) == <span class="hljs-string">'object'</span>) {
            body = <span class="hljs-built_in">JSON</span>.stringify(body);
        }

        <span class="hljs-keyword">const</span> content = [
            app_secret,
            <span class="hljs-string">'access_token'</span> + access_token,
            <span class="hljs-string">'app_key'</span> + app_key,
            <span class="hljs-string">'method'</span> + path,
            <span class="hljs-string">'param_json'</span> + body,
            <span class="hljs-string">'timestamp'</span> + timestamp,
            <span class="hljs-string">'v2.0'</span>,
            app_secret
        ].join(<span class="hljs-string">''</span>);

        <span class="hljs-keyword">const</span> sign_ = <span class="hljs-keyword">await</span> <span class="hljs-keyword">this</span>.sign(algorithm, content, app_secret);

        <span class="hljs-keyword">const</span> queries = {
            <span class="hljs-string">'LOP-DN'</span>: domain,
            <span class="hljs-string">'app_key'</span>: app_key,
            <span class="hljs-string">'access_token'</span>: access_token,
            <span class="hljs-string">'timestamp'</span>: timestamp,
            <span class="hljs-string">'v'</span>: <span class="hljs-string">'2.0'</span>,
            <span class="hljs-string">'sign'</span>: sign_,
            <span class="hljs-string">'algorithm'</span>: algorithm
        };

        <span class="hljs-keyword">const</span> offset = <span class="hljs-built_in">String</span>(-<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTimezoneOffset() / <span class="hljs-number">60</span>);

        <span class="hljs-keyword">const</span> options = {
            method: method,
            headers: {
                <span class="hljs-string">'lop-tz'</span>: offset,
                <span class="hljs-string">'User-Agent'</span>: <span class="hljs-string">'lop-http/nodejs'</span>
            }
        };

        <span class="hljs-keyword">const</span> url = <span class="hljs-string">`<span class="hljs-subst">${base_uri}</span><span class="hljs-subst">${path}</span>?<span class="hljs-subst">${querystring.stringify(queries)}</span>`</span>;

        <span class="hljs-keyword">return</span> url;

        <span class="hljs-keyword">const</span> req = https.request(url, options, <span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, res.statusCode);
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'headers:'</span>, res.headers);

            res.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">d</span>) =&gt;</span> {
                process.stdout.write(d);
            });
        });

        req.on(<span class="hljs-string">'error'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
            <span class="hljs-built_in">console</span>.error(e);
        });

        req.write(body);
        req.end();


    }</code></pre><h3><a id="toc-174" class="anchor" href="#toc-174"></a>第四步：调用所需的接口</h3>
<p>如<a href="https://cloud.jdl.com/#/open-business-document/api-doc/267/842">预下单接口</a>和<a href="https://cloud.jdl.com/#/open-business-document/api-doc/267/841">正式下单接口</a>。</p>
<h3><a id="toc-1bb" class="anchor" href="#toc-1bb"></a>注意事项</h3>
<ul>
<li>调用接口文档的时候需要注意的是请求参数之间彼此是关联的，需要仔细阅读文档，防止调用不成功，具体还是可以使用<a href="https://cloud.jdl.com/#/open-business-document/common-development-tools/267/api-test">常用开发工具</a>来进行调试。</li>
</ul>
<p><img src="https://www.daguanren.cc/static/upload/20230827/upload_73c7669ef63dc1b565282dd1e30dfb34.png" alt="WechatIMG4774.jpg"></p>
<ul>
<li>在物流轨迹订阅 (commonSubscribeTraceV1)时，需要注意，下单后需要间隔大概1分钟再订阅。否则可能订阅失败。参考：<a href="https://cloud.jdl.com/#/open-business-document/api-doc/267/1038">物流轨迹订阅 (commonSubscribeTraceV1)</a></li>
</ul>
<p><img src="https://www.daguanren.cc/static/upload/20230827/upload_600046c04c28e20c62ee9a88e3d30777.png" alt="WechatIMG4772.png"></p>
<p>如果顾客刚一下单你就进行订阅，那么就会返回：</p>
<pre><code class="hljs lang-json">{
    <span class="hljs-attr">"code"</span>:<span class="hljs-number">1206002</span>,
    <span class="hljs-attr">"mills"</span>:<span class="hljs-number">10</span>,
    <span class="hljs-attr">"msg"</span>:<span class="hljs-string">"获取订单详情失败"</span>,
    <span class="hljs-attr">"requestId"</span>:<span class="hljs-string">"ec7df5c0-92c4-4a13-b59b-5c4b599c8cfd"</span>,
    <span class="hljs-attr">"subMsg"</span>:<span class="hljs-string">"order not exists:根据自定义单号未查到订单"</span>,
    <span class="hljs-attr">"success"</span>:<span class="hljs-literal">false</span>
}</code></pre><p>这个时候就很尴尬，一定要注意。在测试时，一定要下单后，等待至少1分钟再进行订阅，订阅成功后，当客户取消订单，或者快递小哥取消订单时才能收到京东快递服务器推送给我们的同步消息。</p>
<ul>
<li>预发环境下的订单有可能也是正式的订单（记不太清了），不管是是预发环境还是正式环境测试时最好备注是测试订单，和快递小哥提前沟通好，否则可能给快递小哥造成不必要的麻烦</li>
</ul>

            ]]></description>
            <pubDate>Sun, 27 Aug 2023 06:51:52 GMT</pubDate>
            <guid>https://www.daguanren.cc/post/jing-dong-kuai-di-jie-kou-dui-jie.html</guid>
        </item>
        <item>
            <title>有关视频流媒体的深入探讨</title>
            <link>https://www.daguanren.cc/post/you-guan-shi-pin-liu-mei-ti-de-shen-ru-tan-tao.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-56f">mp4转m3u8流媒体</a></li>
<li><a href="#toc-b9e">转流媒体的好处</a></li>
<li><a href="#toc-d12">流媒体转换代码和工具</a></li>
<li><a href="#toc-f21">流媒体转换示例</a></li>
<li><a href="#toc-477">视频网站如何压缩的？</a></li>
<li><a href="#toc-5fa">初创企业视频解决方案的建议</a></li>
<li><a href="#toc-3f0">微信小程序解决方案</a></li>
<li><a href="#toc-de5">微信小程序具体实现</a></li>
<li><a href="#toc-73c">微信小程序的DRM加密</a></li>
<li><a href="#toc-e00">DRM许可证服务器是啥？</a></li>
<li><a href="#toc-469">小程序权限控制和许可证服务器的区别</a></li>
<li><a href="#toc-723">有授权才能播放流媒体</a></li>
<li><a href="#toc-e0e">DRM和TLS的区别</a></li>
<li><a href="#toc-3b9">那怎么防止录屏呢？</a></li>
<li><a href="#toc-09b">未完待续</a></li>
</ul>
</div><h3><a id="toc-56f" class="anchor" href="#toc-56f"></a>mp4转m3u8流媒体</h3>
<p><strong>我现在有很多拍的mp4格式的文件，如何像腾讯视频和B站一样转成m3u8这种类型的流媒体来播放？是需要我自己开发播放器吗？</strong></p>
<!--more-->

<p>将MP4视频转换为M3U8流媒体格式并进行播放的过程确实涉及一些技术步骤。你不需要从头开始开发播放器，但是需要使用一些工具和服务。以下是更具体的步骤：</p>
<ol>
<li>转换视频文件到M3U8格式</li>
</ol>
<p>你可以使用FFmpeg这样的工具来将MP4转换为M3U8。一个基本的命令可能如下所示：</p>
<pre><code class="language-bash">ffmpeg -i input.mp4 -profile:v baseline -level 3.0 -s 640x360 -start_number 0 -hls_time 10 -hls_list_size 0 -f hls index.m3u8</code></pre>
<p>这将创建一个M3U8文件和相关的TS分段。</p>
<ol start="2">
<li>托管M3U8文件和TS分段</li>
</ol>
<p>你需要将这些文件放在Web服务器上，以便观众能够访问。你可以使用常见的Web服务器软件如Apache或Nginx。</p>
<ol start="3">
<li>在Web页面中嵌入播放器</li>
</ol>
<p>有许多现成的HTML5视频播放器支持M3U8流，例如Video.js、HLS.js等。你可以将其中一个嵌入到Web页面中，并指向你的M3U8文件。</p>
<p>以下是使用Video.js的一个例子：</p>
<pre><code class="language-html">&lt;video id=&quot;my_video&quot; class=&quot;video-js&quot; controls preload=&quot;auto&quot; width=&quot;640&quot; height=&quot;264&quot;&gt;
  &lt;source src=&quot;index.m3u8&quot; type=&quot;application/x-mpegURL&quot;&gt;
&lt;/video&gt;

&lt;script src=&quot;https://vjs.zencdn.net/7.8.4/video.js&quot;&gt;&lt;/script&gt;
&lt;script&gt;
  var player = videojs(&#39;my_video&#39;);
  player.play();
&lt;/script&gt;</code></pre>
<ol start="4">
<li>测试</li>
</ol>
<p>确保在不同的浏览器和设备上测试你的流，以确保一切正常工作。</p>
<p>注意</p>
<ul>
<li>HLS流在某些旧版本的浏览器或操作系统上可能不受支持。你可能需要使用一些polyfills或fallbacks来确保广泛的兼容性。</li>
<li>如果你要为大量观众提供流，你可能还需要考虑CDN和缓存策略，以确保可扩展性和性能。</li>
<li>请确保遵循所有相关的法律和许可协议，特别是与版权和隐私有关的内容。</li>
</ul>
<p>这些步骤应该提供一个从MP4开始到通过Web播放HLS流的完整路径。当然，具体实现可能会有所不同，取决于你的需求和环境。</p>
<h3><a id="toc-b9e" class="anchor" href="#toc-b9e"></a>转流媒体的好处</h3>
<p><strong>为什么要将视频转成流媒体，这样做有什么好处呢？是怕别人直接下载你的视频吗？还是说这种比较节省资源呢？</strong></p>
<p>将视频转换为流媒体格式有许多优势，不仅限于防止直接下载或节省资源。以下是一些主要优点：</p>
<ol>
<li><p><strong>适应性比特率流 (Adaptive Bitrate Streaming, ABR):</strong> 通过流媒体，服务器可以提供不同质量的视频流，并根据用户的网络速度动态调整。这确保了在低带宽环境下的流畅播放，同时也可以在高带宽环境下提供高质量的体验。</p>
</li>
<li><p><strong>提前开始播放:</strong> 用户不必等待整个文件下载完毕就可以开始观看视频。这提供了更快的启动时间和更好的用户体验。</p>
</li>
<li><p><strong>节省带宽:</strong> 通过流媒体，只传输用户实际观看的部分，而不是整个文件。如果用户在观看一部分视频后停止观看，那么服务器不必发送整个文件，从而节省带宽。</p>
</li>
<li><p><strong>增强的安全性:</strong> 虽然流媒体并不是防止视频被下载的完全保障（有专门工具可以捕获流媒体内容），但它确实增加了难度。它可以与DRM（数字权利管理）等技术结合使用，以进一步保护内容。</p>
</li>
<li><p><strong>更好的兼容性和可扩展性:</strong> 流媒体技术通常支持多种设备和网络条件。通过使用标准的流媒体协议（如HLS或DASH），内容提供商可以更容易地向各种客户端提供内容。</p>
</li>
<li><p><strong>实时或近实时传输:</strong> 对于直播流，流媒体提供了实时或近实时的视频传输能力。这对于直播事件、体育赛事等场景至关重要。</p>
</li>
<li><p><strong>个性化和分析:</strong> 流媒体允许更精细的用户行为分析，如观看时长、暂停位置等。这可以为内容提供商提供更多关于观众参与度的洞见，并允许更个性化的体验。</p>
</li>
<li><p><strong>配合CDN使用:</strong> 流媒体可以更好地与内容分发网络（CDN）配合，实现全球范围内的快速、可靠的内容传输。</p>
</li>
</ol>
<p>总的来说，流媒体提供了更优的观看体验、更高的效率和更灵活的内容交付。这些因素使它成为现代在线视频的首选方法。</p>
<h3><a id="toc-d12" class="anchor" href="#toc-d12"></a>流媒体转换代码和工具</h3>
<p><strong>ffmpeg -i input.mp4 -profile:v baseline -level 3.0 -s 640x360 -start_number 0 -hls_time 10 -hls_list_size 0 -f hls index.m3u8
这个代码是啥意思？是将input.mp4转换成了什么？转换成了哪些文件呢？</strong></p>
<p>这段代码使用了<code>ffmpeg</code>命令行工具，将一个输入的MP4文件（<code>input.mp4</code>）转换成了HLS（HTTP Live Streaming）格式的流媒体文件。下面详细解释每个参数的作用：</p>
<ul>
<li><strong><code>-i input.mp4</code></strong>: 指定输入文件的路径，这里是<code>input.mp4</code>。</li>
<li><strong><code>-profile:v baseline</code></strong>: 设置视频编码的配置文件为基线配置（baseline profile），这通常用于确保最大的兼容性，特别是对于旧设备和网络条件较差的环境。</li>
<li><strong><code>-level 3.0</code></strong>: 指定编码级别，与选定的配置文件协同工作，用于进一步确定编码的约束和功能。</li>
<li><strong><code>-s 640x360</code></strong>: 设置输出视频的分辨率为640x360像素。</li>
<li><strong><code>-start_number 0</code></strong>: 设置HLS播放列表（m3u8文件）中的片段序号从0开始。</li>
<li><strong><code>-hls_time 10</code></strong>: 设置每个HLS片段的持续时间为10秒。这意味着每个TS文件（传输流文件）将包含10秒的视频。</li>
<li><strong><code>-hls_list_size 0</code></strong>: 设置HLS播放列表文件（m3u8文件）的大小。设置为0表示播放列表将包括所有片段，并且不会被限制大小。</li>
<li><strong><code>-f hls</code></strong>: 指定输出格式为HLS。</li>
<li><strong><code>index.m3u8</code></strong>: 指定输出m3u8文件的名称和路径。这个文件包含了指向各个片段（.ts文件）的引用。</li>
</ul>
<p>转换完成后，将会产生一个<code>index.m3u8</code>文件和多个与之关联的<code>.ts</code>文件。每个<code>.ts</code>文件包含了视频的一部分（在这个例子中是10秒），而<code>index.m3u8</code>文件包含了这些<code>.ts</code>文件的索引，以便于播放器按顺序播放它们。</p>
<p>此命令执行的结果是将输入的MP4文件转换为适合流式传输的格式，使得用户可以在下载的同时观看视频，或者根据网络速度自动调整视频质量等。</p>
<h3><a id="toc-f21" class="anchor" href="#toc-f21"></a>流媒体转换示例</h3>
<p><strong>比如一个大小为1个G的视频，转换成流媒体后，视频大小会压缩吗？我看B站等视频网站很多720p和1080p的视频用软件下载下来都会比较小。他们是如何压缩的呢？</strong></p>
<p>转换视频到流媒体格式不一定会导致文件大小的压缩。大小的压缩取决于编码参数和设置，包括但不限于视频分辨率、编码质量、比特率等。</p>
<ol>
<li><p><strong>分辨率</strong>: 通过降低分辨率（例如从1080p降低到720p）可以减小文件大小，但可能会牺牲一些图像质量。</p>
</li>
<li><p><strong>编码质量</strong>: 一些编码器允许你选择质量设置，通常以CRF（恒定速率因子）值表示。较高的CRF值通常会导致较低的文件大小，但可能会损失一些质量。</p>
</li>
<li><p><strong>比特率</strong>: 比特率是指视频中每秒的数据量。降低比特率可以显著减小文件大小，但如果降低得过多，可能会导致视频质量下降。</p>
</li>
</ol>
<p>例如，B站 使用了一套复杂的编码和适配流程来适应不同用户的设备和网络条件。他们可能使用了更先进的编码器和特定的编码设置，以在保持可接受的视频质量的同时减小文件大小。使用高效的编码器，如H.264或H.265，可以在保留相同的视觉质量的情况下大大减小文件大小。</p>
<p>如果你想模仿这样的效果，你可以尝试调整上述的编码参数，比如使用FFmpeg的<code>-crf</code>和<code>-b:v</code>选项来控制质量和比特率。在大多数情况下，合理的压缩并不会对观看体验造成太大的影响。</p>
<p>请注意，过度压缩可能会导致视频质量下降，所以找到适当的平衡点是关键。你可能需要进行一些试验，以找到适合你的特定视频和应用场景的最佳设置。</p>
<h3><a id="toc-477" class="anchor" href="#toc-477"></a>视频网站如何压缩的？</h3>
<p><strong>所以视频网站也基本上是使用FFmpeg来压缩的吗？包括一些其他的压缩软件，都是使用的FFmpeg？</strong></p>
<p>B站和许多其他的大型视频平台可能使用的是专门定制的编码和压缩解决方案。虽然FFmpeg是一款非常强大和流行的开源工具，适用于许多编码和转码任务，但对于大型企业级操作，可能会选择更特定、可定制的解决方案。</p>
<p>有些压缩软件可能确实在底层使用FFmpeg。FFmpeg的库能够被其他软件和系统集成，因此许多开发人员和公司可能会选择使用它来处理视频和音频编码任务。</p>
<p>B站和其他大型平台可能还使用了一些先进的技术和算法，用于更精细的控制和优化，以适应他们的特定需求和规模。这些可能包括自适应比特率流、专门的硬件加速、云端分布式编码等。</p>
<p>简而言之，虽然FFmpeg是一款流行的工具，但不是所有的公司和平台都一定使用它。大型企业可能有资源和需求来开发或购买更专门化的解决方案。</p>
<h3><a id="toc-5fa" class="anchor" href="#toc-5fa"></a>初创企业视频解决方案的建议</h3>
<p><strong>对于我们一家初创企业，你的建议是什么？我们是一家做培训教育的公司，会生产一些培训视频教程。这些视频我们如何提供给付费用户来观看？</strong></p>
<p>对于一家做培训教育的初创企业，你们的视频内容战略会是核心的一部分。以下是一些具体建议，帮助你们提供付费用户观看的视频：</p>
<ol>
<li><p><strong>内容保护和授权管理</strong>：</p>
<ul>
<li>使用数字权利管理（DRM）或其他加密技术确保内容的安全性。</li>
<li>使用用户授权和身份验证来控制谁可以访问特定内容。</li>
</ul>
</li>
<li><p><strong>视频格式和编码</strong>：</p>
<ul>
<li>你可以考虑使用开源工具，例如FFmpeg，来转码视频到适合在线观看的格式，例如HLS（HTTP Live Streaming）。</li>
<li>适应不同设备和网络条件的自适应比特率流也是个不错的选择。</li>
</ul>
</li>
<li><p><strong>内容分发网络（CDN）</strong>：</p>
<ul>
<li>为了确保良好的用户体验，可以考虑使用CDN来分发内容。这可以提高加载速度并减轻服务器的负担。</li>
</ul>
</li>
<li><p><strong>付费和订阅模型</strong>：</p>
<ul>
<li>你可以使用第三方支付网关来处理付费和订阅。有些解决方案专为订阅服务而设计。</li>
</ul>
</li>
<li><p><strong>集成现有的视频平台</strong>：</p>
<ul>
<li>如果你不想自行处理所有这些技术细节，可以考虑使用现有的视频托管平台，如Vimeo Pro或Wistia等。它们提供了许多上述功能，并可以轻松嵌入你的网站。</li>
</ul>
</li>
<li><p><strong>用户体验和界面设计</strong>：</p>
<ul>
<li>最后但同样重要的是，确保你的用户界面易于使用，美观，以及与你的品牌和使命相一致。</li>
</ul>
</li>
<li><p><strong>遵守法律规定</strong>：</p>
<ul>
<li>根据你的运营地点和市场，确保你遵守有关隐私、版权和其他相关法律的规定。</li>
</ul>
</li>
</ol>
<p>通过将这些要素结合在一起，你可以创建一个安全、高效、用户友好的视频教程平台，满足你的付费用户的需求。如果可能的话，与一位有经验的技术顾问或团队合作可能是明智的选择，以确保你的解决方案既符合你的业务需求，也符合最佳实践和行业标准。</p>
<h3><a id="toc-3f0" class="anchor" href="#toc-3f0"></a>微信小程序解决方案</h3>
<p><strong>微信小程序有相关的解决方案吗？</strong></p>
<p>当然，微信小程序提供了一系列的API和组件，可以支持在小程序中嵌入和播放视频内容。对于做培训教育的公司，可以开发微信小程序来提供付费视频课程。以下是一些关键方面的解决方案：</p>
<ol>
<li><p><strong>视频播放组件</strong>：
微信小程序提供了<code>&lt;video&gt;</code>组件，可以用于在小程序中播放视频文件。你可以设置各种属性，例如自动播放、循环播放、全屏等。</p>
</li>
<li><p><strong>用户认证和授权</strong>：
可以通过微信的登录和授权机制，实现对付费用户的身份验证。</p>
</li>
<li><p><strong>支付集成</strong>：
微信支付是小程序中常用的支付方式，可以用于处理付费课程的购买。</p>
</li>
<li><p><strong>视频文件的存储和分发</strong>：
可以使用云存储服务来存储视频文件，并使用内容分发网络（CDN）来确保高效的视频加载。</p>
</li>
<li><p><strong>视频安全</strong>：
通过设置适当的CORS策略和使用签名URL，可以增加视频文件的安全性，确保只有授权的用户可以访问。</p>
</li>
<li><p><strong>自适应流媒体</strong>：
可以考虑将视频转码为适合在线观看的格式，例如HLS，以适应不同的网络环境和设备。</p>
</li>
<li><p><strong>用户体验和界面设计</strong>：
微信小程序提供了丰富的UI组件和样式，可以设计吸引人的用户界面。</p>
</li>
<li><p><strong>法律和合规性</strong>：
同样要确保遵守所有适用的法律和规定，特别是有关隐私和消费者权益的法律。</p>
</li>
</ol>
<p>通过综合以上方面，微信小程序确实为提供付费视频教程提供了一个可行的平台。不过，实现这些功能可能需要一些开发经验和专业知识，因此可能需要与有经验的开发人员或团队合作。此外，不同的业务需求可能还需要进一步的定制和集成。在开始项目之前进行充分的需求分析和规划将是明智的选择。</p>
<h3><a id="toc-de5" class="anchor" href="#toc-de5"></a>微信小程序具体实现</h3>
<p><strong>微信小程序如何实现流媒体呢？</strong></p>
<p>在微信小程序中实现流媒体播放主要涉及以下几个方面：</p>
<ol>
<li><p><strong>选择合适的流媒体格式</strong>：流媒体通常采用一些可以渐进下载和播放的格式，例如HLS（HTTP Live Streaming）。这允许视频在下载的同时开始播放，并可以根据用户的网络条件自适应地调整质量。</p>
</li>
<li><p><strong>准备视频文件</strong>：你可以使用像FFmpeg这样的工具将视频文件转换为适合流媒体的格式。例如，使用HLS，你可以将视频文件分割成一系列较小的文件，然后通过M3U8文件索引它们。</p>
</li>
<li><p><strong>存储和分发视频文件</strong>：流媒体文件应存储在一个可以快速分发到用户的位置。通常，这意味着使用内容分发网络（CDN）或云存储服务。</p>
</li>
<li><p><strong>嵌入视频播放器</strong>：微信小程序提供了<code>&lt;video&gt;</code>组件，允许在小程序中嵌入和控制视频播放。只需将HLS的M3U8文件的URL设置为视频源即可。</p>
</li>
<li><p><strong>用户认证和访问控制</strong>：如果视频是付费内容，你可能还需要实现一些身份验证和授权机制，以确保只有付费和授权的用户可以访问视频。</p>
</li>
<li><p><strong>优化用户体验</strong>：可以通过设置合适的缓冲策略、提供多个质量选项、添加控制和界面反馈等来优化用户体验。</p>
</li>
<li><p><strong>分析和监控</strong>：可能还希望收集一些分析数据，例如用户观看时间、播放质量等，以便了解用户的观看习惯并优化服务。</p>
</li>
</ol>
<p>以下是一个微信小程序中使用<code>&lt;video&gt;</code>组件播放HLS流媒体的示例代码：</p>
<pre><code class="language-html">&lt;video 
  src=&quot;https://example.com/path/to/index.m3u8&quot; 
  controls 
  autoplay 
  loop 
  poster=&quot;https://example.com/path/to/placeholder.jpg&quot;
&gt;&lt;/video&gt;</code></pre>
<p>请注意，流媒体播放可能涉及许多技术和业务考虑因素，因此根据具体需求和环境可能需要进一步的定制和集成。如果你不熟悉流媒体技术，可能需要考虑聘请专业开发人员或使用第三方流媒体服务。</p>
<h3><a id="toc-73c" class="anchor" href="#toc-73c"></a>微信小程序的DRM加密</h3>
<p>小程序文档里的DRM 加密播放是什么？</p>
<pre><code class="hljs lang-qml">以下是小程序的官方文档，DRM 加密播放是干嘛的？有什么用呢？请详细解释下

DRM 加密播放
小程序开发者获取到 DRM 加密的 视频地址、身份认证 <span class="hljs-built_in">url</span>、license <span class="hljs-built_in">url</span>
使用 video 标签将以上几个参数填入
小程序确认该 video 为 DRM 视频源，进行 DRM 设备身份认证并且获取播放许可证
设备身份认证通过并获取播放许可证之后，由 DRM 底层进行解密播放

Q&amp;A
Q：为什么设备身份认证 <span class="hljs-built_in">url</span> 要区分 Android 和 iOS ？

A：由于 Android 和 iOS 是基于不同的 DRM 协议，Android：widevine；iOS：fairplay，所以身份认证这块有所不同，需要分别提供身份认证 <span class="hljs-built_in">url</span>。

Q：license <span class="hljs-built_in">url</span> 的格式是什么样的？

A：目前 license <span class="hljs-built_in">url</span> 需要支持标准 license 回包，即裸 license</code></pre><p>DRM（数字版权管理）是一种用于保护数字内容版权的技术。在视频播放领域，DRM用于确保只有已授权的用户能够播放受保护的视频内容，并防止未经授权的复制和分发。以下是DRM加密播放在微信小程序中的具体作用和用途的详细解释：</p>
<ol>
<li><p><strong>保护视频内容</strong>：通过加密视频文件，DRM确保了只有具有合适许可证的设备才能解密和播放视频。这可以有效防止未经授权的人员访问、下载、复制和分发受保护的内容。</p>
</li>
<li><p><strong>设备身份认证</strong>：DRM系统需要验证设备的身份，以确保它有权访问受保护的内容。这通常涉及与远程服务器的交互，其中服务器根据设备提供的凭证和/或用户的登录信息确定设备是否有权访问内容。</p>
</li>
<li><p><strong>获取播放许可证</strong>：设备身份认证通过后，设备可以从许可证服务器获取播放许可证。这个许可证包括解密视频所需的密钥以及可能的其他限制，例如播放期限、允许的播放次数等。</p>
</li>
<li><p><strong>兼容性问题</strong>：不同的设备和操作系统可能使用不同的DRM方案。例如，在小程序文档中提到的Android使用Widevine，而iOS使用FairPlay。因此，可能需要为不同的设备和操作系统提供不同的认证URL和许可证URL。</p>
</li>
<li><p><strong>license url的格式</strong>：license url用于从许可证服务器获取播放许可证。它需要支持标准的license回包，即“裸license”，意味着license应按照特定格式直接返回，而不应嵌入其他数据或结构。</p>
</li>
</ol>
<p>总的来说，DRM加密播放为微信小程序提供了一种方法来播放受版权保护的视频内容，同时确保只有已授权的用户和设备能够访问和播放该内容。这对于付费视频、独家内容或任何其他需要严格版权控制的场景非常重要。</p>
<h3><a id="toc-e00" class="anchor" href="#toc-e00"></a>DRM许可证服务器是啥？</h3>
<p><strong>许可证服务器是啥？谁负责管理的？比如我们的视频如何申请？</strong></p>
<p>许可证服务器是一个专门的服务器，负责存储和分发数字版权管理（DRM）许可证。这些许可证包括解密受保护内容所需的密钥和关于如何使用该内容的规则（例如观看期限、允许的设备数量等）。许可证服务器与DRM客户端（例如用户的设备或应用程序）交互，以验证用户的身份并提供适当的许可证。</p>
<p>关于谁负责管理和如何申请，以下是一些可能的场景：</p>
<ol>
<li><p><strong>自己托管许可证服务器</strong>：一些组织选择自己托管和管理许可证服务器。这可能涉及购买和配置专门的DRM解决方案，并与内容分发网络（CDN）和/或媒体服务器集成。这种方法提供了最大的灵活性，但也可能相对复杂和昂贵。</p>
</li>
<li><p><strong>使用第三方DRM提供商</strong>：许多初创企业和小型组织选择与第三方DRM提供商合作。这些提供商提供完整的DRM服务，包括许可证服务器托管、内容加密、用户身份验证等。这种方法通常更快、更便宜，并减少了技术复杂性。</p>
</li>
<li><p><strong>申请过程</strong>：具体的申请过程可能因DRM提供商和解决方案而异。通常，您需要与提供商联系，解释您的需求和用例，并选择合适的服务计划。他们将协助您设置和集成DRM，包括提供必要的密钥、认证URL和许可证URL。</p>
</li>
<li><p><strong>内容加密</strong>：在设置许可证服务器之后，您还需要对您的视频内容进行加密，并确保播放器能够与许可证服务器交互以获取解密密钥。这可能涉及使用特定的工具或库，并可能需要一些技术专长。</p>
</li>
</ol>
<p>对于培训教育的初创企业来说，与第三方DRM提供商合作可能是一个合理的选择，特别是如果您不具备大量的技术资源或对DRM不熟悉。许多提供商专门服务于小型和中型企业，并提供易于使用的工具和支持来帮助您保护您的内容。</p>
<h3><a id="toc-469" class="anchor" href="#toc-469"></a>小程序权限控制和许可证服务器的区别</h3>
<p><strong>还是不太懂，不是在小程序上播放吗？我们可以在小程序上判断这个用户有没有登录和看视频的权限呀。为啥还要搞个许可证服务器呢？</strong></p>
<p>我理解您的困惑，特别是当您已经在小程序上进行了用户身份验证和权限检查时。让我们深入了解许可证服务器和DRM加密播放在内容保护方面的作用，以及为什么它们可能对您有用。</p>
<ol>
<li><p><strong>基本权限检查</strong>：在小程序中进行的用户身份验证和权限检查确实可以控制谁可以访问和观看视频。然而，一旦用户访问了视频文件，他们可能会尝试下载、复制或以其他方式非法共享该内容。基本的身份验证并不阻止这样的行为。</p>
</li>
<li><p><strong>DRM（数字版权管理）</strong>：DRM旨在保护内容不仅在传输过程中，还在播放过程中。通过使用许可证服务器和特定的加密技术，DRM确保只有经过授权的设备和应用程序可以播放内容。即使用户设法下载加密的文件，他们也无法在未经授权的播放器上观看它。</p>
</li>
<li><p><strong>许可证服务器的作用</strong>：许可证服务器扮演了核心角色，因为它管理着解密内容所需的密钥。当用户尝试观看受保护的视频时，播放器将与许可证服务器通信，验证用户的身份和权限，并获取解密密钥。如果用户没有适当的权限，许可证服务器将拒绝提供密钥，从而防止播放。</p>
</li>
<li><p><strong>为什么使用DRM</strong>：如果您的内容非常重要，且您不希望它被非法复制和共享，那么DRM可能是一个合适的解决方案。尤其是对于付费内容，防止未经授权的访问和分发可以保护您的收入和知识产权。</p>
</li>
<li><p><strong>权衡考虑</strong>：DRM确实增加了复杂性，并可能涉及额外的成本。不是每个场景都需要它。如果您的内容不是高价值或敏感，或者您更关心用户体验而不是安全性，那么简单的身份验证和权限检查可能就足够了。</p>
</li>
</ol>
<p>总而言之，许可证服务器和DRM提供了更高层次的内容保护，超出了基本的身份验证和权限检查所能提供的范围。是否需要它取决于您的具体需求和关注点。</p>
<h3><a id="toc-723" class="anchor" href="#toc-723"></a>有授权才能播放流媒体</h3>
<p><strong>哦，是不是这样理解，比如我的视频发布在小程序上，别人通过某些手段，比如我的视频是m3u8的，别人通过下载m3u8的所有ts文件后拼接成了完整的视频，然后别人拿着这个视频要播放的话，是不是如果没有授权就播放不了？我好像理解的不太对。</strong></p>
<p>您的理解接近正确，但是还有一些细微的差异，让我更详细地解释一下。</p>
<ol>
<li><p><strong>加密过程</strong>：当您使用DRM（数字版权管理）保护视频时，视频文件会被加密。对于使用m3u8格式的流媒体，这意味着各个ts文件片段会被加密。</p>
</li>
<li><p><strong>播放过程</strong>：要播放受保护的视频，播放器必须与许可证服务器通信以获取解密密钥。只有在满足许可条件的情况下，服务器才会提供密钥。</p>
</li>
<li><p><strong>非法下载的问题</strong>：如果某人设法下载了所有加密的ts文件并尝试将它们拼接在一起，他们还是无法观看视频，因为文件是加密的。除非他们有正确的解密密钥（这只能通过许可证服务器获得），否则他们无法解密视频。</p>
</li>
<li><p><strong>拷贝与共享的问题</strong>：由于DRM加密，即使有人可以下载整个视频文件，他们也无法在没有正确许可证的其他设备或播放器上播放它。这样就防止了未经授权的复制和共享。</p>
</li>
</ol>
<p>所以，总结一下，您的理解基本正确。通过使用DRM和许可证服务器，您可以确保即使有人下载了视频的所有片段，他们也无法观看或共享整个视频，除非他们通过了合适的身份验证和授权过程。这增强了您的内容保护，并可以确保只有付费或合格的用户能够访问视频。</p>
<h3><a id="toc-e0e" class="anchor" href="#toc-e0e"></a>DRM和TLS的区别</h3>
<p><strong>DRM这个感觉和tls有点像呢</strong></p>
<p>确实，DRM（数字版权管理）与TLS（传输层安全协议）在某些方面有相似之处，尤其是它们都涉及加密和身份验证。以下是它们之间的一些相似和不同之处：</p>
<p>相似之处：</p>
<ol>
<li><p><strong>加密</strong>：两者都使用加密来保护数据。DRM加密内容，以防止未授权访问，而TLS加密在网络上传输的数据，以保护隐私和完整性。</p>
</li>
<li><p><strong>身份验证</strong>：两者都涉及身份验证过程。在DRM中，许可证服务器验证请求许可证的设备或用户的身份。在TLS中，服务器证书帮助客户端验证服务器的身份。</p>
</li>
<li><p><strong>密钥管理</strong>：两者都涉及密钥的生成、交换和使用，以实现加密和解密过程。</p>
</li>
</ol>
<p>不同之处：</p>
<ol>
<li><p><strong>目的和应用领域</strong>：DRM主要用于保护数字内容，如音乐、电影和电子书，防止未经授权的复制和分发。TLS主要用于保护网络通信，确保在互联网上发送的数据的机密性和完整性。</p>
</li>
<li><p><strong>实施细节</strong>：虽然都涉及加密，但DRM和TLS在如何实现和管理加密方面有所不同。DRM可能涉及更复杂的许可模型，而TLS更关注端到端的连接安全。</p>
</li>
<li><p><strong>技术和协议</strong>：DRM和TLS使用不同的技术和协议来实现它们的目标。例如，TLS使用的是一套标准化的安全协议，而DRM可能涉及各种专有或标准化的技术，具体取决于所用的DRM系统。</p>
</li>
</ol>
<p>总的来说，尽管DRM和TLS在某些方面有所相似，但它们解决的问题和应用的场景有所不同。不过，它们确实都是信息安全领域的重要组成部分。</p>
<h3><a id="toc-3b9" class="anchor" href="#toc-3b9"></a>那怎么防止录屏呢？</h3>
<p><strong>明白了，这也是一种保护手段。如果客户通过录屏，其实还是可以将视频保存下来。</strong></p>
<p>是的，你完全正确。DRM（数字版权管理）是一种保护数字内容的手段，旨在防止未经授权的访问和分发。然而，就像许多技术保护措施一样，它并不是完全无懈可击的。</p>
<p>录屏技术可以绕过DRM保护，通过捕获正在播放的视频的屏幕图像，从而创建内容的未经授权的副本。虽然这种方法可能导致一些质量损失（例如分辨率降低、可能出现的水印等），但它确实是一种可以突破DRM保护的方式。</p>
<p>因此，虽然DRM可以大大提高内容保护的门槛，使得大规模的非法分发更加困难，但它并不能阻止所有形式的侵权。对于希望保护其内容的公司来说，DRM可以作为一个有效的屏障，但还需要结合其他方法，如法律手段、水印技术、监控分析等，以形成更全面的保护机制。</p>
<h3><a id="toc-09b" class="anchor" href="#toc-09b"></a>未完待续</h3>

            ]]></description>
            <pubDate>Sun, 20 Aug 2023 06:26:11 GMT</pubDate>
            <guid>https://www.daguanren.cc/post/you-guan-shi-pin-liu-mei-ti-de-shen-ru-tan-tao.html</guid>
        </item>
        <item>
            <title>如何获取关注公众号的链接</title>
            <link>https://www.daguanren.cc/post/ru-he-huo-qu-guan-zhu-gong-zhong-hao-de-lian-jie.html</link>
            <description><![CDATA[
            <div class="toc"></div><p>H5页面上关注公众号比较麻烦，最常见的只能通过在H5上放置一个公众号的二维码，然后长按识别关注公众号。</p>
<!--more-->


<p>有没有什么方法能够在H5上可以直接关注公众号，又不用扫码呢？</p>
<p>答案是有的。先看下实现效果：</p>
<img alt="*.png" src="https://www.daguanren.cc/static/upload/20230817/upload_2e5638ccec8f360168a71b4ce432c71f.png" width="500" height="">

<p>在微信打开链接，直接跳转到历史消息页面，在历史消息页面就可以点击关注进行公众号的关注，关注后，按钮会变成发消息，点击发消息即可进入公众号页面。</p>
<p>那么这个链接如何获取呢？</p>
<p>首先打开你想要获取关注的公众号，看下有没有发过文章，如果有的话随便打开一篇该公众号发过的文章。</p>
<p>例如，“光翰科技”公众号里有这样一篇文章：<a href="https://mp.weixin.qq.com/s/TFTJ7xTT62M2xRiTwR8EXQ">https://mp.weixin.qq.com/s/TFTJ7xTT62M2xRiTwR8EXQ</a></p>
<p>将这篇文章用浏览器打开，然后点击查看源代码：</p>
<img alt="*.png" src="https://www.daguanren.cc/static/upload/20230817/upload_deafd51916aadcf1f6710d2b2b666281.png" width="700" height="">

<p>搜索__biz，找到这一行：</p>
<pre><code class="hljs lang-xml">_g.msg_link = "http://mp.weixin.qq.com/s?__biz=MzUzNjQxMjQ1Mw==<span class="hljs-symbol">&amp;amp;</span>mid=2247484434<span class="hljs-symbol">&amp;amp;</span>idx=1<span class="hljs-symbol">&amp;amp;</span>sn=4c20933947e10110e6c4e651a1048ecb<span class="hljs-symbol">&amp;amp;</span>chksm=faf7ec58cd80654e2e2246192f3272b27ca42e2042b224e23b8ebfa1e6933c8e1cba92bd4621#rd";</code></pre><p>将=号和;号之间的文本全部复制，替换掉下面的中文部分。</p>
<pre><code class="hljs lang-awk">https:<span class="hljs-regexp">//m</span>p.weixin.qq.com<span class="hljs-regexp">/mp/</span>profile_ext?action=home&amp;__biz=这里替换&amp;scene=<span class="hljs-number">110</span><span class="hljs-comment">#wechat_redirect</span></code></pre><p>形成最终的url：</p>
<pre><code class="hljs lang-sas">https://mp.weixin.qq.com/mp/profile_ext?action=home<span class="hljs-variable">&amp;__biz</span>=MzUzNjQxMjQ1Mw==<span class="hljs-variable">&amp;mid</span>=2247484434<span class="hljs-variable">&amp;idx</span>=1<span class="hljs-variable">&amp;sn</span>=4c20933947e10110e6c4e651a1048ecb<span class="hljs-variable">&amp;chksm</span>=faf7ec58cd80654e2e2246192f3272b27ca42e2042b224e23b8ebfa1e6933c8e1cba92bd4621#rd<span class="hljs-variable">&amp;scene</span>=110#wechat_redirect</code></pre><p>然后将此url放置到H5上即可。</p>
<p>另外，小程序上也可以直接关注公众号。具体可以看见这篇文章：</p>
<p><a href="https://www.sunzhongwei.com/wechat-applet-shown-concern-the-public-number-button">微信小程序中显示关注公众号按钮</a></p>
<p>参考：</p>
<p><a href="https://developers.weixin.qq.com/community/minihome/doc/0000aa2e3b08b0c6fb4fe01285b800?source=indexmixflow">公众号在哪里获得链接，让别人点击链接跳转到关注页面？</a></p>

            ]]></description>
            <pubDate>Thu, 17 Aug 2023 11:41:05 GMT</pubDate>
            <guid>https://www.daguanren.cc/post/ru-he-huo-qu-guan-zhu-gong-zhong-hao-de-lian-jie.html</guid>
        </item>
        <item>
            <title>Midjourney创建的图片可以商用吗？</title>
            <link>https://www.daguanren.cc/post/Midjourney-ni-yong-dui-le-ma.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-d5a">商用问题</a></li>
<li><a href="#toc-5cc">官方服务条款(以官网最新版为准)</a></li>
<li><a href="#toc-25f">总结</a></li>
</ul>
</div><p>Midjourney的官网是可以直接打开的，包括官网的文档和官网的Showcase：</p>
<!--more-->

<img alt="*.png" src="https://www.daguanren.cc/static/upload/20230816/upload_1f7e20d4ce7cb3716702a6666dd3bed8.png" width="500" height="">

<img alt="*.png" src="https://www.daguanren.cc/static/upload/20230816/upload_9a8410bb9e89776a0bd0be69fadae6b0.png" width="500" height="">

<p>但是你要创建你的图片就需要登录，登录目前只能通过discord这个软件，但这个软件是无法使用的。所以目前想要使用Midjourney创建图片，可能去尝试QQ的内测申请和等待QQ的公测了。可参见：<a href="https://zhuanlan.zhihu.com/p/643422374">年赚1亿美金，只有11个人，Midjourney做对了什么？</a></p>
<h3><a id="toc-d5a" class="anchor" href="#toc-d5a"></a>商用问题</h3>
<p>假设我们已经可以正常使用midjourney。那么有几个问题我们应该关注下：</p>
<ol>
<li><p>我们创建的图片可以直接商用吗？</p>
<ul>
<li>如果我们是免费的用户，创建的图片可以商用吗？</li>
<li>如果我们是付费用户呢？</li>
<li>如果我们是付费用户创建的，但是后面我不付费了，还可以商用吗？</li>
</ul>
</li>
</ol>
<ol start="2">
<li><p>放大别人的图片或者直接复制别人创建的图片也可以商用吗？</p>
</li>
<li><p>midjourney在大陆使用创建的图片也受保护吗？</p>
</li>
</ol>
<h3><a id="toc-5cc" class="anchor" href="#toc-5cc"></a>官方服务条款(以官网最新版为准)</h3>
<p>要回答这个问题，我们必须得看下官网文档有关我们权利的定义，参考<a href="https://docs.midjourney.com/docs/terms-of-service">Midjouney文档的Your Rights描述</a>。</p>
<img alt="*.png" src="https://www.daguanren.cc/static/upload/20230816/upload_d941341c3c2b0b5811b30c13d48a49ab.png" width="600" height="">

<p>这是midjourney服务的权利条款，大致意思如下：</p>
<ol>
<li><p>你对自己创建的资产拥有所有权：如果你按照本协议使用该服务创建了资产（如图像、视频等），你将拥有这些资产的所有权。但是，这不包括将他人的图像放大的情况，这些图像仍由原始资产创建者拥有。对于法律适用情况，midjourney不提供任何保证或承诺，如果你想了解更多关于你所在地法律的信息，建议你咨询律师。即使你在未来降级或取消会员资格，你对所创建资产的所有权仍然保持。</p>
</li>
<li><p>大公司的使用要求：如果你是年收入超过100万美元的公司的员工或所有者，并且代表你的雇主使用该服务，你必须为每个代表你使用该服务的人购买“Pro”或“Mega”会员才能拥有你创建的资产。如果你不确定你的使用是否代表你的雇主，请假定它是的。</p>
</li>
<li><p>未付费会员的资产权利：如果你不是付费会员，你不拥有你创建的资产。但是，Midjourney将根据Creative Commons Noncommercial 4.0 Attribution International License授权给你资产的使用权。完整的文本可以在以下链接找到：<a href="https://creativecommons.org/licenses/by-nc/4.0/legalcode%E3%80%82">https://creativecommons.org/licenses/by-nc/4.0/legalcode。</a></p>
</li>
<li><p>Midjourney是一个开放的社区：其他人可以使用和重新混合你在公共环境中发布的图像和提示。默认情况下，你的图像是公开可查看和可重新混合的。购买&quot;Pro&quot;或&quot;Mega&quot;计划的用户可以绕过一些这些公共共享默认设置。</p>
</li>
<li><p>Stealth特性：如果你购买了Stealth特性作为你的“Pro”或&quot;Mega&quot;订阅的一部分，或通过之前可用的附加组件购买，当你在服务中启用Stealth模式时，我们同意尽最大努力不在任何情况下发布你制作的资产。</p>
</li>
<li><p>注意事项：请注意，任何你在共享或公开空间（如Discord聊天室）中制作的图像都可被该聊天室中的任何人查看，无论是否启用Stealth模式。</p>
</li>
</ol>
<p>总之，Midjourney的权利条款规定了在使用该服务时对创建的资产的所有权和许可权的不同情况。在某些情况下，例如在公共环境中发布的图像和提示，还存在其他用户使用和重新混合的可能性。</p>
<p>所以Midjourney的权利条款中提到的<a href="https://creativecommons.org/licenses/by-nc/4.0/legalcode">Creative Commons Noncommercial 4.0 Attribution International License</a>也很关键。</p>
<p>Creative Commons Attribution-NonCommercial 4.0 International Public License（简称CC BY-NC 4.0）是一种公共许可证，允许您在非商业目的下使用许可资料。以下是该许可证的主要内容：</p>
<ol>
<li>许可范围：</li>
</ol>
<ul>
<li>允许您在全球范围内、免版税、不可转让、非独占、不可撤销地行使许可资料的许可权。</li>
<li>您可以复制和分享许可资料（整体或部分）仅用于非商业目的。</li>
<li>您可以制作、复制和分享改编资料仅用于非商业目的。</li>
</ul>
<ol start="2">
<li>许可条件：</li>
</ol>
<ul>
<li>如果您分享许可资料（包括修改后的形式），您必须：<ul>
<li>保留许可人提供的许可资料的相关信息（如创建者标识、版权通知、许可证引用等）。</li>
<li>指示您修改了许可资料，并保留对其进行的任何先前修改的说明。</li>
<li>指示许可资料在本公共许可证下获得许可，并包含本公共许可证的文本或链接。</li>
</ul>
</li>
</ul>
<ol start="3">
<li>非商业定义：</li>
</ol>
<ul>
<li>非商业定义为不主要针对或朝向商业利益或货币补偿。在本公共许可证下，通过数字文件共享或类似方式交换许可资料与其他受版权和类似权利保护的资料，只要不涉及货币补偿的支付，都被视为非商业行为。</li>
</ul>
<ol start="4">
<li>免责声明和责任限制：</li>
</ol>
<ul>
<li>许可人在尽可能的范围内提供许可资料“按原样”和“按现状”，不对许可资料作任何形式的陈述或保证。许可人在尽可能的范围内不对因本公共许可证或许可资料的使用而产生的任何损失、费用、开支或损害承担责任。</li>
</ul>
<ol start="5">
<li>许可期限和终止：</li>
</ol>
<ul>
<li>本公共许可证适用于许可的版权和类似权利的期限。但是，如果您未遵守本公共许可证，那么您在本公共许可证下的权利将自动终止。</li>
</ul>
<h3><a id="toc-25f" class="anchor" href="#toc-25f"></a>总结</h3>
<p>好了，那么重点总结来了：</p>
<ul>
<li>如果我们是免费的用户，创建的图片可以商用吗？</li>
</ul>
<p><strong>根据这个许可证，如果您不付费，您仍然可以使用创建的图片，但只能用于非商业目的。您不能将这些图片用于商业利益或货币补偿的目的。此外，您必须遵守许可条件，包括正确归属和标注许可资料。</strong></p>
<ul>
<li>如果我们是付费用户呢？</li>
</ul>
<p><strong>如果您是付费会员，并且遵守Midjourney的服务条款，您将拥有您使用服务创建的所有资产的所有权。</strong></p>
<ul>
<li>如果我们是付费用户创建的，但是后面我不付费了，还可以商用吗？</li>
</ul>
<p><strong>如果您不再付费，您仍然拥有您在付费期间创建的资产的所有权。根据提供的Midjourney权利条款的描述，当你付费使用服务并创建的资产（在这里指的是图片），只要它们是在遵守服务协议的情况下创建的，你将拥有这些资产的所有权。这种所有权将在你降级或取消会员资格后仍然存在。因此，即使你后来不再付费，你之前在付费期间创建的图片仍然可以商用。</strong></p>
<ul>
<li>放大别人的图片或者直接复制别人创建的图片也可以商用吗？</li>
</ul>
<p><strong>不能。你使用的图片应该是你自己创建的或者你有权使用的。如果你使用了他人的图片进行放大处理，则这些图片仍然属于原始资产创建者。</strong></p>
<ul>
<li>midjourney在大陆使用创建的图片也受保护吗？</li>
</ul>
<p><strong>一般来说，无论在哪里使用Midjourney服务创建的资产，都会享有与服务条款中描述的相同的版权。但是，由于不同国家和地区的版权法规定不同，你在中国大陆使用该服务创建的资产的版权可能会受到中国大陆法律的限制或影响。</strong></p>
<p><strong>总的来说，如果你在中国大陆使用Midjourney服务，可能会面临服务不稳定、不受官方支持、存在法律风险等问题，建议在了解相关法律规定并充分评估风险后谨慎使用。</strong></p>
<p>有些服务是可以访问，但不对咱们提供服务。有些服务则是无法访问：</p>
<img alt="*.png" src="https://www.daguanren.cc/static/upload/20230816/upload_cd4fa6ec0fe926c9aea2a13d739c6302.png" width="600" height="">

            ]]></description>
            <pubDate>Wed, 16 Aug 2023 01:39:23 GMT</pubDate>
            <guid>https://www.daguanren.cc/post/Midjourney-ni-yong-dui-le-ma.html</guid>
        </item>
        <item>
            <title>微信网页开发-网页授权-获取用户信息</title>
            <link>https://www.daguanren.cc/post/wei-xin-wang-ye-kai-fa---wang-ye-shou-quan---huo-qu-yong-hu-xin-xi.html</link>
            <description><![CDATA[
            <div class="toc"></div><p>微信的隐私保护政策越来越严格，微信网页授权中获取用户信息的接口也不断调整，虽然官网也有文档，但是你会发现实际调用接口的时候，接口返回的数据与官网描述的返回字段并不匹配，因为官网文档的更新具有滞后性，此篇博文主要记录微信网页开发-网页授权-获取用户信息的实际返回字段。</p>
<!--more-->

<p>首先，官方文档的访问链接在此：<a href="https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html">网页授权</a></p>
<p>这里获取用户信息所需要用到的access_token和公众号定时刷新的access_token不是同一个。这里的access_token，我并没有保存和定时刷新，因为没有看到获取限制。每个用户每次登录都能获取到的access_token都是不同的。</p>
<p><img src="https://www.daguanren.cc/static/upload/20221028/upload_600d60fd918f74b13b690288de05dd5a.png" alt="image.png"></p>
<p>可以看到，目前微信返回的有效的用户信息，仅有头像、昵称、openid和unionid返回不是空。</p>
<p>但是奇怪的是，我这里选择的是snsapi_base，并不是snsapi_userinfo，依然可以调用此接口：</p>
<pre><code class="hljs lang-awk"><span class="hljs-comment">## 第四步：拉取用户信息(需 scope 为 snsapi_userinfo)</span>

https:<span class="hljs-regexp">//</span>api.weixin.qq.com<span class="hljs-regexp">/sns/u</span>serinfo?access_token=ACCESS_TOKEN&amp;openid=OPENID&amp;lang=zh_CN</code></pre>
            ]]></description>
            <pubDate>Fri, 28 Oct 2022 02:07:35 GMT</pubDate>
            <guid>https://www.daguanren.cc/post/wei-xin-wang-ye-kai-fa---wang-ye-shou-quan---huo-qu-yong-hu-xin-xi.html</guid>
        </item>
    </channel>
</rss>
