feat: 开源项目门户站首版(iotLCM / 秀杰 DTU / XiuAdmin 三项目,注册表驱动)
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
.vitepress/cache
|
||||||
|
.vitepress/dist
|
||||||
|
.vitepress/.temp
|
||||||
|
dist
|
||||||
|
*.log
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { defineConfig } from 'vitepress'
|
||||||
|
import { projects } from '../data/projects'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 门户站构建配置
|
||||||
|
*
|
||||||
|
* SITE_BASE 由构建参数注入:
|
||||||
|
* - /www/ 过渡形态:挂在已有网关的 /www/ 路径下(没有独立域名时用)
|
||||||
|
* - / 独立域名形态:站点独占域名根路径
|
||||||
|
* 两者内容完全同源,只有资源前缀与路由前缀不同,所以用构建参数而不是改代码。
|
||||||
|
*/
|
||||||
|
const SITE_BASE = process.env.SITE_BASE ?? '/www/'
|
||||||
|
|
||||||
|
/** 代码仓组织(雪羊科技企业仓) */
|
||||||
|
const ORG_URL = process.env.ORG_URL ?? 'https://gitee.com/snowgoat'
|
||||||
|
|
||||||
|
/** 各项目的文档站地址(按项目 id 覆盖) */
|
||||||
|
const DOCS_URL: Record<string, string> = {
|
||||||
|
iotlcm: 'http://open.5iot.com:5051/docs/',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
srcDir: './pages',
|
||||||
|
base: SITE_BASE,
|
||||||
|
title: '雪羊科技开源',
|
||||||
|
description: '雪羊科技开源项目门户:物联网卡管理、DTU 设备管理、中后台开发框架',
|
||||||
|
lang: 'zh-CN',
|
||||||
|
cleanUrls: true,
|
||||||
|
lastUpdated: false,
|
||||||
|
head: [
|
||||||
|
['link', { rel: 'icon', type: 'image/svg+xml', href: `${SITE_BASE}favicon.svg` }],
|
||||||
|
],
|
||||||
|
themeConfig: {
|
||||||
|
nav: [
|
||||||
|
{ text: '首页', link: '/' },
|
||||||
|
{ text: '项目', link: '/projects/' },
|
||||||
|
{ text: '关于', link: '/about' },
|
||||||
|
],
|
||||||
|
sidebar: {
|
||||||
|
'/projects/': [
|
||||||
|
{
|
||||||
|
text: '项目',
|
||||||
|
collapsed: false,
|
||||||
|
items: projects.map((p) => ({ text: p.name, link: p.page })),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'/guide/': [
|
||||||
|
{
|
||||||
|
text: '门户',
|
||||||
|
collapsed: false,
|
||||||
|
items: [{ text: '门户部署', link: '/guide/portal-deploy' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
socialLinks: [
|
||||||
|
{ icon: 'github', link: ORG_URL },
|
||||||
|
],
|
||||||
|
footer: {
|
||||||
|
message: '雪羊科技开源 · Apache-2.0',
|
||||||
|
copyright: 'Copyright © 雪羊科技',
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
provider: 'local',
|
||||||
|
},
|
||||||
|
outline: {
|
||||||
|
level: [2, 3],
|
||||||
|
label: '本页目录',
|
||||||
|
},
|
||||||
|
docFooter: {
|
||||||
|
prev: '上一页',
|
||||||
|
next: '下一页',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { Project } from '../../../data/projects'
|
||||||
|
|
||||||
|
defineProps<{ project: Project }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a :href="project.page" class="project-card">
|
||||||
|
<div class="project-card-head">
|
||||||
|
<h3>{{ project.name }}</h3>
|
||||||
|
<span class="project-status" :data-status="project.status">{{ project.status }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="project-tagline">{{ project.tagline }}</p>
|
||||||
|
<p class="project-summary">{{ project.summary }}</p>
|
||||||
|
<ul class="project-tech">
|
||||||
|
<li v-for="t in project.tech" :key="t">{{ t }}</li>
|
||||||
|
</ul>
|
||||||
|
<div class="project-foot">
|
||||||
|
<span class="project-license">{{ project.license }}</span>
|
||||||
|
<span class="project-link">查看详情 →</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.project-card {
|
||||||
|
display: block;
|
||||||
|
padding: 20px 22px;
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--vp-c-bg-soft);
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
transition: border-color 0.2s, transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card:hover {
|
||||||
|
border-color: var(--vp-c-brand-1);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-status {
|
||||||
|
flex: none;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-status[data-status='已发布'] {
|
||||||
|
border-color: var(--vp-c-brand-1);
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-tagline {
|
||||||
|
margin: 8px 0 0;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-summary {
|
||||||
|
margin: 8px 0 0;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-tech {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
margin: 14px 0 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-tech li {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--vp-c-default-soft);
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-foot {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--vp-c-text-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-link {
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { projects } from '../../../data/projects'
|
||||||
|
import ProjectCard from './ProjectCard.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="project-list">
|
||||||
|
<ProjectCard v-for="p in projects" :key="p.id" :project="p" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.project-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
margin: 24px 0 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import DefaultTheme from 'vitepress/theme'
|
||||||
|
import type { Theme } from 'vitepress'
|
||||||
|
|
||||||
|
import ProjectList from './components/ProjectList.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
extends: DefaultTheme,
|
||||||
|
enhanceApp({ app }) {
|
||||||
|
app.component('ProjectList', ProjectList)
|
||||||
|
},
|
||||||
|
} satisfies Theme
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
# 开源项目门户(VitePress)构建镜像
|
||||||
|
#
|
||||||
|
# 构建参数:
|
||||||
|
# SITE_BASE —— 站点资源前缀,同时决定容器内 nginx 的形态:
|
||||||
|
# /www/ 过渡形态:挂在已有网关的 /www/ 路径下(没有独立域名时用)
|
||||||
|
# / 独立域名形态:站点独占域名根路径
|
||||||
|
#
|
||||||
|
# 前缀与 nginx 形态必须配套:VitePress 产物里的资源引用是绝对路径
|
||||||
|
# (<base>assets/xxx),base=/www/ 时只能由带 alias 的 location 提供服务;
|
||||||
|
# base=/ 时只能由根路径 location 提供服务。混用会出现「页面 200 但 JS/CSS 全 404」。
|
||||||
|
#
|
||||||
|
# 用法(仓库根目录):
|
||||||
|
# docker build -t snowgoat-open-portal:dev --build-arg SITE_BASE=/www/ .
|
||||||
|
FROM node:22-alpine AS builder
|
||||||
|
|
||||||
|
ARG SITE_BASE=/www/
|
||||||
|
|
||||||
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
|
||||||
|
&& apk add --no-cache git
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json .npmrc ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN SITE_BASE=${SITE_BASE} npm run build
|
||||||
|
|
||||||
|
FROM nginx:1.27-alpine
|
||||||
|
|
||||||
|
ARG SITE_BASE=/www/
|
||||||
|
|
||||||
|
COPY --from=builder /app/.vitepress/dist /usr/share/nginx/html
|
||||||
|
COPY docker/nginx.root.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY docker/nginx.prefix.conf /tmp/nginx.prefix.conf
|
||||||
|
RUN set -eu; \
|
||||||
|
base="${SITE_BASE%/}"; \
|
||||||
|
if [ "$base" = "" ]; then \
|
||||||
|
echo "门户形态:独立域名(根路径)"; \
|
||||||
|
else \
|
||||||
|
sed -e "s|__SITE_BASE__|${SITE_BASE}|g" -e "s|__SITE_BASE_NO_SLASH__|${base}|g" \
|
||||||
|
/tmp/nginx.prefix.conf > /etc/nginx/conf.d/default.conf; \
|
||||||
|
echo "门户形态:子路径 ${SITE_BASE}"; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# 雪羊科技开源项目门户
|
||||||
|
|
||||||
|
开源项目门户站源码。当前收录 **iotLCM**(物联网卡全生命周期管理平台)、**秀杰 DTU 管理平台**、**XiuAdmin**(中后台开发框架)。
|
||||||
|
|
||||||
|
- 站点框架:VitePress 1.6(与 iotLCM 的文档站同构)
|
||||||
|
- 产物:纯静态文件
|
||||||
|
- 许可:Apache-2.0
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
|
||||||
|
```text
|
||||||
|
.
|
||||||
|
├── data/projects.ts # 项目注册表 —— 门户唯一项目清单来源
|
||||||
|
├── pages/
|
||||||
|
│ ├── index.md # 门户首页
|
||||||
|
│ ├── projects/index.md # 全部项目
|
||||||
|
│ ├── projects/<id>/index.md # 各项目详情页
|
||||||
|
│ ├── guide/portal-deploy.md # 门户自身的构建与部署
|
||||||
|
│ └── about.md # 关于、许可、贡献说明
|
||||||
|
├── .vitepress/theme/components/ # 项目卡片与列表组件
|
||||||
|
├── scripts/check-registry.mjs # 注册表与详情页一致性校验
|
||||||
|
├── deploy/docker-compose.yml # 容器部署编排
|
||||||
|
└── Dockerfile # 多阶段构建镜像
|
||||||
|
```
|
||||||
|
|
||||||
|
## 新增一个项目
|
||||||
|
|
||||||
|
两步,不需要改动站点其它部分:
|
||||||
|
|
||||||
|
1. 在 `data/projects.ts` 里加一条记录(名称、定位、说明、代码仓、许可证、技术栈、状态、详情页路径、子页面)
|
||||||
|
2. 写 `pages/projects/<id>/index.md` 详情页
|
||||||
|
|
||||||
|
首页卡片、项目列表页、侧边栏都会自动带上新项目。
|
||||||
|
|
||||||
|
构建前会自动校验注册表与详情页目录是否一一对应,不一致直接报错:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run check
|
||||||
|
```
|
||||||
|
|
||||||
|
## 本地开发
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci
|
||||||
|
npm run dev # http://127.0.0.1:5173
|
||||||
|
```
|
||||||
|
|
||||||
|
## 构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build # 产物在 .vitepress/dist/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 部署
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f deploy/docker-compose.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
默认发布在宿主机 `127.0.0.1:8020`。站点资源前缀由 `SITE_BASE` 决定:
|
||||||
|
|
||||||
|
| 形态 | 参数 | 访问方式 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 子路径(过渡,无独立域名) | `/www/`(默认) | 由已有网关转发 `/www/` 到本容器 |
|
||||||
|
| 独立域名 | `/` | 域名根路径 |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SITE_BASE=/ docker compose -f deploy/docker-compose.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
详见 [门户部署](pages/guide/portal-deploy.md)。
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* 开源项目注册表 —— 门户的唯一项目清单来源。
|
||||||
|
*
|
||||||
|
* 新增一个项目只需两步:
|
||||||
|
* 1) 在这里加一条记录
|
||||||
|
* 2) 写 pages/projects/<id>/index.md 详情页,并在 sections 里登记它的子页面
|
||||||
|
* 首页卡片、项目列表、侧边栏、导航都会自动带上,不需要改任何其它文件。
|
||||||
|
* 校验:npm run check(构建前自动执行,详情页与注册表不一致会直接报错)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type ProjectStatus = '已发布' | '公测中' | '开发中'
|
||||||
|
|
||||||
|
export interface ProjectSection {
|
||||||
|
text: string
|
||||||
|
link: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Project {
|
||||||
|
/** 目录名与 URL 片段,一经发布不再改动(改名会断链) */
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
/** 一句话定位,展示在卡片标题下 */
|
||||||
|
tagline: string
|
||||||
|
/** 两三句说明,展示在卡片正文 */
|
||||||
|
summary: string
|
||||||
|
/** 开源代码仓 */
|
||||||
|
repo: string
|
||||||
|
/** 许可证标识 */
|
||||||
|
license: string
|
||||||
|
/** 技术栈标签 */
|
||||||
|
tech: string[]
|
||||||
|
status: ProjectStatus
|
||||||
|
/** 门户内的详情页路径 */
|
||||||
|
page: string
|
||||||
|
/** 详情页下的子页面(快速开始、部署、教程等) */
|
||||||
|
sections: ProjectSection[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const projects: Project[] = [
|
||||||
|
{
|
||||||
|
id: 'iotlcm',
|
||||||
|
name: 'iotLCM',
|
||||||
|
tagline: '物联网卡全生命周期管理平台',
|
||||||
|
summary:
|
||||||
|
'卡片从运营商渠道入库 → 划拨给代理商 → 激活 / 续费 / 停机 → 流量与状态同步 → 销户,一个平台覆盖 SIM 卡管理全程。内置中国移动 OneLink、中国电信 5G CMP、中国联通 10646 协议适配,并支持以共享 Token 模式接入多家三方渠道商。',
|
||||||
|
repo: 'https://gitee.com/snowgoat/iotlcm',
|
||||||
|
license: 'Apache-2.0',
|
||||||
|
tech: ['GoFrame v2', 'Vue 3', 'MySQL', 'RabbitMQ', 'TDengine', 'Docker Compose'],
|
||||||
|
status: '已发布',
|
||||||
|
page: '/projects/iotlcm/',
|
||||||
|
sections: [
|
||||||
|
{ text: '快速开始', link: '/projects/iotlcm/guide/quickstart' },
|
||||||
|
{ text: '部署与运维', link: '/projects/iotlcm/guide/deploy' },
|
||||||
|
{ text: '教程视频', link: '/projects/iotlcm/guide/tutorial' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'xiu-dtu',
|
||||||
|
name: '秀杰 DTU 管理平台',
|
||||||
|
tagline: 'DTU 设备全生命周期管理平台',
|
||||||
|
summary:
|
||||||
|
'面向 DTU(数据传输单元)设备,覆盖设备生产、出厂、代理商划拨、终端客户绑定、配置下发、固件 OTA 升级、指令下发与运行监控的完整闭环,HTTP / TCP 双通道接入。',
|
||||||
|
repo: 'https://gitee.com/snowgoat/xiu-dtu',
|
||||||
|
license: 'Apache-2.0',
|
||||||
|
tech: ['GoFrame v2', 'Vue 3 + Vben Admin', 'MySQL', 'Redis', 'asynq', 'VitePress'],
|
||||||
|
status: '已发布',
|
||||||
|
page: '/projects/xiu-dtu/',
|
||||||
|
sections: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'xiu-admin',
|
||||||
|
name: 'XiuAdmin',
|
||||||
|
tagline: 'GoFrame + Vue3 的全栖中后台框架',
|
||||||
|
summary:
|
||||||
|
'高生产率、可扩展、应用化 / 模块化 / 插件化的中后台开发框架。租户、用户、部门、菜单、角色、代码生成、定时任务、消息队列、通知公告等 22 项内置功能开箱可用。',
|
||||||
|
repo: 'https://gitee.com/snowgoat/xiu-admin',
|
||||||
|
license: 'Apache-2.0',
|
||||||
|
tech: ['GoFrame v2', 'Vue 3 + Vben Admin', 'Ant Design Vue', 'asynq'],
|
||||||
|
status: '已发布',
|
||||||
|
page: '/projects/xiu-admin/',
|
||||||
|
sections: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
/** 按 id 取项目,找不到返回 undefined */
|
||||||
|
export function projectById(id: string): Project | undefined {
|
||||||
|
return projects.find((p) => p.id === id)
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# 门户部署
|
||||||
|
|
||||||
|
## 启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f deploy/docker-compose.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
默认发布在宿主机 `127.0.0.1:8020`(用 `PORTAL_PORT` 改端口)。
|
||||||
|
|
||||||
|
## 两种形态
|
||||||
|
|
||||||
|
构建参数 `SITE_BASE` 决定站点资源前缀,同时决定容器内 nginx 的形态:
|
||||||
|
|
||||||
|
| 形态 | 参数 | 访问方式 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 子路径(过渡,无独立域名) | `/www/`(默认) | 由已有网关转发 `/www/` 到本容器 |
|
||||||
|
| 独立域名 | `/` | 域名根路径,如 `https://<门户域名>/` |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SITE_BASE=/ docker compose -f deploy/docker-compose.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
内容同源,切换只改构建参数与入口配置,页面不用改。
|
||||||
|
|
||||||
|
> VitePress 产物里的资源引用是绝对路径(`<base>assets/xxx`),base 与 nginx 的 location 形态必须配套,
|
||||||
|
> 混用会出现「页面 200 但 JS/CSS 全 404」的白屏。镜像构建脚本按 `SITE_BASE` 自动选对应配置。
|
||||||
|
|
||||||
|
## 校验
|
||||||
|
|
||||||
|
门户项目清单与详情页的一致性在构建前自动校验:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run check
|
||||||
|
```
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# 开源项目门户部署编排
|
||||||
|
#
|
||||||
|
# docker compose -f deploy/docker-compose.yml up -d --build
|
||||||
|
#
|
||||||
|
# 形态切换(站点资源前缀):
|
||||||
|
# SITE_BASE=/www/ 子路径形态,挂在已有网关的 /www/ 路径下(默认)
|
||||||
|
# SITE_BASE=/ 独立域名形态,站点独占域名根路径
|
||||||
|
#
|
||||||
|
# SITE_BASE=/ docker compose -f deploy/docker-compose.yml up -d --build
|
||||||
|
services:
|
||||||
|
portal:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
SITE_BASE: '${SITE_BASE:-/www/}'
|
||||||
|
image: snowgoat-open-portal:local
|
||||||
|
container_name: snowgoat-open-portal
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:${PORTAL_PORT:-8020}:80"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--quiet", "--output-document=/dev/null", "http://127.0.0.1${SITE_BASE:-/www/}"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# 过渡形态:站点挂在统一网关的子路径下(没有独立域名时用)。
|
||||||
|
#
|
||||||
|
# VitePress 产物里的资源引用是绝对路径(<base>assets/xxx),
|
||||||
|
# 而 dist 目录是平铺的(index.html、assets/ 直接在根)。
|
||||||
|
# 因此必须在子路径下用 alias 把前缀剥掉再落到产物目录,
|
||||||
|
# 否则页面能返回 200 但 JS/CSS 全部 404 → 白屏。
|
||||||
|
# 网关侧对应 location ^~ <prefix> { proxy_pass http://gw_website; }(不剥前缀)。
|
||||||
|
location __SITE_BASE__ {
|
||||||
|
alias /usr/share/nginx/html/;
|
||||||
|
try_files $uri $uri.html $uri/ $uri/index.html =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 访问不带尾斜杠的前缀时补斜杠,否则相对资源路径会解析错
|
||||||
|
location = __SITE_BASE_NO_SLASH__ {
|
||||||
|
return 301 __SITE_BASE__;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# 独立域名形态:站点独占域名根路径,产物平铺在根目录。
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri.html $uri/ =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+2534
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "snowgoat-open-portal",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vitepress dev . --host 127.0.0.1",
|
||||||
|
"build": "vitepress build .",
|
||||||
|
"preview": "vitepress preview . --host 127.0.0.1",
|
||||||
|
"check": "node scripts/check-registry.mjs",
|
||||||
|
"prebuild": "npm run check",
|
||||||
|
"predev": "npm run check"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vitepress": "^1.6.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
title: 关于
|
||||||
|
description: 雪羊科技开源项目门户说明
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 关于
|
||||||
|
|
||||||
|
雪羊科技开源项目门户汇集公司对外发布的开源项目。当前收录物联网卡管理平台(iotLCM)、DTU 设备管理平台(秀杰 DTU)与中后台开发框架(XiuAdmin)。
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
全部项目以 **Apache-2.0** 发布。可自由使用、修改、商用与再分发;分发时保留版权声明与许可证原文,并对修改部分注明变更。各项目依赖的上游组件保留其原始许可。
|
||||||
|
|
||||||
|
## 参与贡献
|
||||||
|
|
||||||
|
1. 从目标项目的默认分支切出特性分支,命名 `feat/xxx`、`fix/xxx`
|
||||||
|
2. 提交前确保各自的构建与测试通过(Go 服务跑 `go build ./...` 与 `go test ./...`,前端跑 `pnpm build`)
|
||||||
|
3. 提交合入请求并说明变更动机
|
||||||
|
|
||||||
|
## 门户本身
|
||||||
|
|
||||||
|
门户是一个独立的 VitePress 工程,与平台代码不在同一个仓库。构建产物是纯静态站点,可用任意静态服务器或容器托管。
|
||||||
|
|
||||||
|
镜像构建与部署说明见[门户部署](/guide/portal-deploy)。
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
title: 门户部署
|
||||||
|
description: 开源项目门户的构建与部署方式
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 门户部署
|
||||||
|
|
||||||
|
门户是一个独立的 VitePress 工程,产物是纯静态文件。
|
||||||
|
|
||||||
|
## 本地构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
产物在 `.vitepress/dist/`。
|
||||||
|
|
||||||
|
## 容器部署
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f deploy/docker-compose.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
默认发布在宿主机 `127.0.0.1:8020`,容器内监听 80。
|
||||||
|
|
||||||
|
## 两种路径形态
|
||||||
|
|
||||||
|
构建参数 `SITE_BASE` 决定站点资源前缀:
|
||||||
|
|
||||||
|
| 形态 | 参数 | 访问方式 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 子路径(过渡形态,无独立域名) | `/www/`(默认) | 挂在已有网关的 `/www/` 路径下 |
|
||||||
|
| 独立域名 | `/` | 站点独占域名根路径 |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SITE_BASE=/ docker compose -f deploy/docker-compose.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
两者内容完全同源,切换只改构建参数与入口配置,页面不用改。
|
||||||
|
|
||||||
|
> VitePress 产物里的资源引用是绝对路径(`<base>assets/xxx`),base 与 nginx 的 location 形态必须配套,混用会出现「页面 200 但 JS/CSS 全 404」的白屏。镜像构建脚本按 `SITE_BASE` 自动选对应的 nginx 配置。
|
||||||
|
|
||||||
|
## 新增项目
|
||||||
|
|
||||||
|
项目清单由 `data/projects.ts` 单一注册表驱动,新增项目不需要改动站点其它部分:
|
||||||
|
|
||||||
|
1. 在注册表里加一条记录
|
||||||
|
2. 写 `pages/projects/<id>/index.md` 详情页
|
||||||
|
|
||||||
|
构建前会自动校验注册表与详情页目录是否一一对应。
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
layout: home
|
||||||
|
|
||||||
|
hero:
|
||||||
|
name: 雪羊科技开源
|
||||||
|
text: 物联网与中后台基础软件
|
||||||
|
tagline: 开源项目门户 · Apache-2.0
|
||||||
|
actions:
|
||||||
|
- theme: brand
|
||||||
|
text: 浏览全部项目
|
||||||
|
link: /projects/
|
||||||
|
- theme: alt
|
||||||
|
text: iotLCM 快速开始
|
||||||
|
link: /projects/iotlcm/guide/quickstart
|
||||||
|
|
||||||
|
features:
|
||||||
|
- title: 全生命周期管理
|
||||||
|
details: iotLCM 覆盖物联网 SIM 卡的入库、划拨、激活、续费、停机、流量与状态同步、销户全流程。
|
||||||
|
- title: 设备接入闭环
|
||||||
|
details: 秀杰 DTU 管理平台覆盖 DTU 设备生产、出厂、划拨、绑定、配置下发、固件 OTA 与指令下发。
|
||||||
|
- title: 中后台开发底座
|
||||||
|
details: XiuAdmin 是基于 GoFrame + Vue3 的全栖中后台框架,租户、权限、代码生成、消息队列等内置可用。
|
||||||
|
- title: 一条命令部署
|
||||||
|
details: 各项目均提供 Docker Compose 编排与预构建镜像,clone 之后即可跑起来。
|
||||||
|
- title: 统一 Apache-2.0
|
||||||
|
details: 全部项目以 Apache-2.0 发布,可自由使用、修改、商用与再分发。
|
||||||
|
- title: 持续新增
|
||||||
|
details: 门户项目清单由单一注册表驱动,新增项目不需要改动站点其它部分。
|
||||||
|
---
|
||||||
|
|
||||||
|
## 开源项目
|
||||||
|
|
||||||
|
<ProjectList />
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
- 每个项目在「项目详情」页给出定位、能力边界与代码仓地址
|
||||||
|
- 部署与运维细节见各项目的指南页
|
||||||
|
- 源码仓统一托管在 Gitee 企业仓 `snowgoat` 命名空间下
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: 全部项目
|
||||||
|
description: 雪羊科技开源项目清单
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 全部项目
|
||||||
|
|
||||||
|
<ProjectList />
|
||||||
|
|
||||||
|
## 关于这份清单
|
||||||
|
|
||||||
|
项目清单由站点源码里的单一注册表维护(`data/projects.ts`),首页卡片、侧边栏与本页都从它生成。
|
||||||
|
|
||||||
|
新增一个项目需要两步:
|
||||||
|
|
||||||
|
1. 在注册表里加一条记录(名称、定位、说明、代码仓、许可证、技术栈、状态)
|
||||||
|
2. 写 `pages/projects/<id>/index.md` 详情页,若有子页面(快速开始、部署、教程)在记录的 `sections` 里登记
|
||||||
|
|
||||||
|
站点构建前会自动校验「注册表条目」与「详情页目录」是否一一对应,不一致直接报错,避免清单与页面脱节。
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
---
|
||||||
|
title: 部署与运维
|
||||||
|
description: 生产部署前的必改项、可选组件、离线部署与常见问题
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 部署与运维
|
||||||
|
|
||||||
|
## 一键启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://gitee.com/snowgoat/iotlcm.git
|
||||||
|
cd iotlcm/deploy
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
部署编排与数据库初始化脚本都在仓库的 `deploy/` 目录:
|
||||||
|
|
||||||
|
```text
|
||||||
|
deploy/
|
||||||
|
├── docker-compose.yml # 服务编排(含默认口令与端口)
|
||||||
|
├── compose.memlimit.yml # 容器内存上限(按机器规格叠加使用)
|
||||||
|
├── config/ # 各服务配置 + gateway / nginx 两种入口配置
|
||||||
|
├── schema/init.sql # MySQL 初始化(95 表 + 种子数据)
|
||||||
|
├── tdengine-init/ # 时序库建库建表(首次启动自动执行)
|
||||||
|
└── data/ # 运行数据,首次启动自动创建
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可选组件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile with-mock up -d # 运营商接口挡板
|
||||||
|
docker compose --profile with-bot up -d # 飞书通知(运维自用)
|
||||||
|
docker compose --profile with-nginx up -d # 自带反向代理(多域名入口)
|
||||||
|
docker compose --profile with-gateway up -d # 单端口入口(默认对外 5051)
|
||||||
|
```
|
||||||
|
|
||||||
|
多个 profile 可组合。
|
||||||
|
|
||||||
|
### 单端口入口(with-gateway)
|
||||||
|
|
||||||
|
只暴露一个端口时用它。容器内监听 80,宿主机端口由 `GATEWAY_PORT` 控制(默认 5051):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile with-gateway up -d
|
||||||
|
# 或换端口
|
||||||
|
GATEWAY_PORT=8088 docker compose --profile with-gateway up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
| 路径 | 去向 |
|
||||||
|
| --- | --- |
|
||||||
|
| `/` | 管理端(admin-ui,内部已代理 `/api/` 到 admin-svr) |
|
||||||
|
| `/docs/` | 文档站 |
|
||||||
|
| `/openapi/` | 对外 API |
|
||||||
|
| `/route/open/` | 运营商能力代理(白名单 / 共享 Token) |
|
||||||
|
|
||||||
|
配置见 `config/gateway/nginx.conf`。小程序 / H5 端的静态资源是根路径部署的,不能挂在同一域名的子路径下,需要单独域名或端口(见 `with-nginx` 的 app 段)。
|
||||||
|
|
||||||
|
`with-bot` 是把全量同步等运维事件推送到飞书群的**内部运维工具**,不是平台功能。它需要你自己的飞书自建应用凭证(`config/bot/config.yaml` 的 `appId` / `appSecret` / `chatId`),没填就不启动。
|
||||||
|
|
||||||
|
**默认不启动,也不影响平台功能**;不需要飞书通知就完全不用管它。
|
||||||
|
|
||||||
|
## 线上暴露到公网
|
||||||
|
|
||||||
|
80 / 443 入站在多数办公宽带上被运营商封禁,可评估三种形态:
|
||||||
|
|
||||||
|
| 形态 | 适用 | 代价 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 高位端口 NAT | 内部验收、演示 | 地址带端口,不适合对外正式发布 |
|
||||||
|
| 独立子域 + 反代 | 有公网 IP 的服务器 | 需要域名解析与证书 |
|
||||||
|
| 云主机统一入口 | 正式对外发布 | 需要域名(以及国内服务器的备案) |
|
||||||
|
|
||||||
|
## 上线前必改
|
||||||
|
|
||||||
|
| 项 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| 数据库口令 | MySQL 的 root 与应用账号口令 |
|
||||||
|
| Redis 口令 | 缓存层口令 |
|
||||||
|
| RabbitMQ 口令 | 队列层口令 |
|
||||||
|
| admin 口令 | 种子账号初始口令统一重置过,必须改 |
|
||||||
|
| JWT 密钥 | 管理端与用户端签发令牌用 |
|
||||||
|
| 运营商通道凭据 | 通道配置的 appKey / appSecret / 账号,按运营商要求申请 |
|
||||||
|
| 对象存储 | 图片与附件的存放位置 |
|
||||||
|
|
||||||
|
## 离线部署
|
||||||
|
|
||||||
|
目标机器不能出网时:
|
||||||
|
|
||||||
|
1. 在有网机器上拉全部镜像并导出:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose pull
|
||||||
|
docker save -o iotlcm-images.tar $(docker compose config --images)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 拷贝 `iotlcm-images.tar` 与仓库的 `deploy/` 目录到目标机器;
|
||||||
|
3. `docker load -i iotlcm-images.tar` 后再 `docker compose up -d`。
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
| 现象 | 原因与处置 |
|
||||||
|
| --- | --- |
|
||||||
|
| MySQL 初始化中途失败 | schema 只在数据卷为空时执行,改完脚本必须清空卷才会重跑:`docker compose down && sudo rm -rf ./data/mysql && docker compose up -d` |
|
||||||
|
| 访问 8019 端口白屏 | 文档站必须带 `/docs/` 路径,裸端口会白屏 |
|
||||||
|
| 偶发 502 | 容器重建后 upstream IP 变化,nginx 未重新解析;配置已带 `resolver` + `resolve`,若仍出现,重启网关容器即可 |
|
||||||
|
| 通道调用报 IP 鉴权失败 | 运营商侧需要报备出口 IP,未报备则拒绝调用 |
|
||||||
|
| 端口被占用 | 编排里业务端口默认绑 `127.0.0.1`,改端口映射即可避开冲突 |
|
||||||
|
|
||||||
|
## 监控
|
||||||
|
|
||||||
|
Consul 提供服务注册与健康探针,Prometheus 指标入口在编排内已开。日志按服务落在 `deploy/data/logs/` 下,配合 logrotate 生效。
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
title: 快速开始
|
||||||
|
description: 三步把 iotLCM 开源版跑起来
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 前置条件
|
||||||
|
|
||||||
|
| 项 | 要求 |
|
||||||
|
| --- | --- |
|
||||||
|
| 操作系统 | Linux(x86_64 / arm64) |
|
||||||
|
| Docker | 20.10 以上,含 Compose V2(`docker compose version` 可用) |
|
||||||
|
| 内存 | 建议 4 GB 以上 |
|
||||||
|
| 磁盘 | 建议 40 GB 以上(含镜像与数据库) |
|
||||||
|
| 端口 | 3306 / 6379 / 5672 / 6041 / 6030 / 8500 默认占用,起步前先确认空闲 |
|
||||||
|
|
||||||
|
## 第一步:取代码
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://gitee.com/snowgoat/iotlcm.git
|
||||||
|
cd iotlcm
|
||||||
|
```
|
||||||
|
|
||||||
|
## 第二步:起栈
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd deploy
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
不需要复制或修改任何文件,编排里已带可用的默认配置。首次启动会自动:
|
||||||
|
|
||||||
|
1. MySQL 建库并导入 schema(13 个脚本按序执行:建表 → 结构补齐 → 创建应用账号 → 重置种子账号口令 → 导入通道模板、字典、菜单等种子数据),约 30 秒 ~ 1 分钟
|
||||||
|
2. TDengine 自动建库建表(流量历史库)
|
||||||
|
3. 其余服务依次启动并向 Consul 注册
|
||||||
|
|
||||||
|
观察进度:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs -f mysql
|
||||||
|
```
|
||||||
|
|
||||||
|
看到 `[initdb] 应用账号 u_iotlcm 已创建` 与 `[initdb] 种子账号口令已统一重置` 表示初始化完成。
|
||||||
|
|
||||||
|
## 第三步:登录
|
||||||
|
|
||||||
|
部署完成后访问 `http://127.0.0.1:8009`,账号 `admin`,初始口令见仓库的 `deploy/README.md`。
|
||||||
|
|
||||||
|
> **正式使用前必须修改默认口令**:数据库、Redis、RabbitMQ、admin 账号口令与 JWT 密钥。清单见[部署与运维](/projects/iotlcm/guide/deploy)的「上线前必改」。
|
||||||
|
|
||||||
|
## 端口对照
|
||||||
|
|
||||||
|
| 服务 | 宿主机端口 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 管理端 | 8009 | Web 管理平台 |
|
||||||
|
| 对外接口 | 8000 | OpenAPI V3 与兼容接口 |
|
||||||
|
| 用户端 | 8018 | 小程序 / H5 |
|
||||||
|
| 文档站 | 8019 | 使用手册与接口文档,必须带 `/docs/` 路径 |
|
||||||
|
| 运营商对接 | 8110 / 8310 | 通道协议适配 |
|
||||||
|
| 通道缓存 | 8510 | 缓存转发 |
|
||||||
|
| MySQL | 3306 | 库名 `db_iotlcm` |
|
||||||
|
| Redis | 6379 | 缓存与运行状态 |
|
||||||
|
| RabbitMQ | 5672 / 15672 | 队列与管理界面 |
|
||||||
|
| TDengine | 6041 / 6030 | 时序库 |
|
||||||
|
| Consul | 8500 | 服务注册 |
|
||||||
|
|
||||||
|
编排默认把业务端口绑定在 `127.0.0.1`,需要局域网或公网访问时,改用反向代理编排或并入宿主机已有的 nginx。
|
||||||
|
|
||||||
|
## 停止与清理
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose down # 停止,数据保留
|
||||||
|
docker compose down -v # 停止并删除数据卷(不可恢复)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 下一步
|
||||||
|
|
||||||
|
- [部署与运维](/projects/iotlcm/guide/deploy)
|
||||||
|
- [教程视频](/projects/iotlcm/guide/tutorial)
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
title: 教程视频
|
||||||
|
description: 部署与使用的录屏教程
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 教程视频
|
||||||
|
|
||||||
|
开源版的上手路径只有两条:**把平台跑起来**(部署)与**把平台用起来**(使用)。两条路径各一支录屏视频,文字文档只作配套说明,不替代视频。
|
||||||
|
|
||||||
|
## 部署教程
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 标题 | iotLCM 开源版部署教程:从零到可用 |
|
||||||
|
| 目标观众 | 第一次接触本项目、具备 Linux 与 Docker 基础的开发者与实施工程师 |
|
||||||
|
| 时长 | 12 ~ 15 分钟 |
|
||||||
|
| 发布位置 | 官网本页 + 代码仓 README 顶部 + B 站 |
|
||||||
|
| 关联文档 | [快速开始](/projects/iotlcm/guide/quickstart)、[部署与运维](/projects/iotlcm/guide/deploy) |
|
||||||
|
| 关联仓库路径 | `deploy/`、`docs/` |
|
||||||
|
|
||||||
|
视频覆盖:
|
||||||
|
|
||||||
|
1. 环境准备:Docker 与 Compose 版本检查、端口占用确认、机器规格建议
|
||||||
|
2. 取代码:`git clone` 公开仓库,确认目录结构
|
||||||
|
3. 启动:`docker compose up -d`,观察 MySQL 初始化日志直到种子数据导入完成
|
||||||
|
4. 验证:登录管理端、打开文档站与 OpenAPI 文档、跑一次状态查询
|
||||||
|
5. 收尾:改默认口令、停栈与清理数据卷
|
||||||
|
|
||||||
|
## 使用教程
|
||||||
|
|
||||||
|
| 项 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 标题 | iotLCM 开源版使用教程:核心功能实操 |
|
||||||
|
| 目标观众 | 已完成部署、准备给业务方上手的管理员与运营人员 |
|
||||||
|
| 时长 | 15 ~ 20 分钟 |
|
||||||
|
| 发布位置 | 官网本页 + 代码仓 README 顶部 + B 站 |
|
||||||
|
| 关联文档 | [功能概览](/projects/iotlcm/) |
|
||||||
|
| 关联仓库路径 | `admin-svr/`、`admin-ui/`、`openapi/`、`worker-svr/` |
|
||||||
|
|
||||||
|
视频覆盖:
|
||||||
|
|
||||||
|
1. 卡信息:列表检索、按三码查卡、卡详情与用量查看
|
||||||
|
2. 库存与划拨:入库后的代理商维度划拨
|
||||||
|
3. 资费与业务受理:套餐配置、开通/续费/停复机、达量断网
|
||||||
|
4. 通道与批次:通道配置查看、批量操作与异步进度
|
||||||
|
5. 对外接口:申请 API 凭证并完成一次真实调用
|
||||||
|
6. 账号与工单:企业用户与角色、告警工单处理
|
||||||
|
|
||||||
|
## 视频文件放置约定
|
||||||
|
|
||||||
|
成片放在代码仓的 `website/public/videos/` 下,文件名用小写连字符加版本号(如 `deploy-tutorial-v1.mp4`),页面里以相对路径引用:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<video controls src="/videos/deploy-tutorial-v1.mp4"></video>
|
||||||
|
```
|
||||||
|
|
||||||
|
若改投 B 站等外部托管,页面保留外链入口即可,视频文件不进仓库(避免仓库体积膨胀)。
|
||||||
|
|
||||||
|
## 录制备忘
|
||||||
|
|
||||||
|
- 录屏环境用与观众一致的环境:干净的 Ubuntu + Docker Compose V2,不要在已有业务实例上录
|
||||||
|
- 涉及口令的片段一律打码,示例口令用编排里的默认值
|
||||||
|
- 步骤与[快速开始](/projects/iotlcm/guide/quickstart)严格一致,视频里出现的每条命令都必须能原样复制执行
|
||||||
|
- 每个视频产出配套字幕或要点说明(Markdown),随视频一起发布
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
title: iotLCM
|
||||||
|
description: 物联网卡全生命周期管理平台
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# iotLCM
|
||||||
|
|
||||||
|
物联网卡全生命周期管理平台。卡片从运营商渠道入库 → 划拨给代理商 → 激活 / 续费 / 停机 → 流量与状态同步 → 销户,一个平台覆盖 SIM 卡管理全程。
|
||||||
|
|
||||||
|
对接**中国移动 OneLink**、**中国电信 5G CMP**、**中国联通 10646** 等运营商接口,支持以共享 Token 模式接入三方渠道商,并提供对外的 OpenAPI 服务。
|
||||||
|
|
||||||
|
| 项 | 值 |
|
||||||
|
| --- | --- |
|
||||||
|
| 代码仓 | <https://gitee.com/snowgoat/iotlcm> |
|
||||||
|
| 许可证 | Apache-2.0 |
|
||||||
|
| 状态 | 已发布 |
|
||||||
|
| 技术栈 | GoFrame v2、Vue 3 + Vben Admin、MySQL、Redis、RabbitMQ、TDengine、Consul、Docker Compose |
|
||||||
|
| 文档站 | <http://open.5iot.com:5051/docs/> |
|
||||||
|
|
||||||
|
## 能力概览
|
||||||
|
|
||||||
|
| 域 | 能力 |
|
||||||
|
| --- | --- |
|
||||||
|
| 卡资产 | 卡信息、批量导入、入库 / 库存 / 出库、批量开停复机、续费、变更套餐、达量断网 |
|
||||||
|
| 通信管理 | 通道与接入模板、运营商能力对接、共享 Token 接入三方渠道、通道缓存转发 |
|
||||||
|
| 资费 | 套餐与流量池、定价、生效与到期策略 |
|
||||||
|
| 代理商 | 代理商与分佣、划拨与回收 |
|
||||||
|
| 数据同步 | 流量 / 状态 / IMEI 全量同步与增量对齐、历史账期时序数据 |
|
||||||
|
| OpenAPI | V3 接口、旧版兼容接口、运营商能力代理(白名单转发 / 共享 Token) |
|
||||||
|
| 运营 | 工单、预警、执行日志、审计、智能诊断 |
|
||||||
|
| 端 | Web 管理端、小程序 / H5 用户端 |
|
||||||
|
|
||||||
|
## 服务构成
|
||||||
|
|
||||||
|
单仓多服务:全部后端服务、前端应用、文档站与部署编排都在同一个仓库里。
|
||||||
|
|
||||||
|
| 目录 | 说明 | 镜像 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `admin-svr/` | 管理端后端(GoFrame) | `iotlcm-admin-svr` |
|
||||||
|
| `admin-ui/` | 管理端前端(Vue 3 + Vben Admin) | `iotlcm-admin-ui` |
|
||||||
|
| `openapi/` | 对外 API 网关 | `iotlcm-openapi` |
|
||||||
|
| `worker-svr/` | 异步执行(RabbitMQ 消费者、全量同步、批量任务) | `iotlcm-worker-svr` |
|
||||||
|
| `upstream-chan/` | 运营商 / 三方渠道对接层 | `iotlcm-upstream-chan` |
|
||||||
|
| `upstream-proxy/` | 通道缓存转发层 | `iotlcm-upstream-proxy` |
|
||||||
|
| `app/server/` | 用户端服务(GoFrame) | `iotlcm-app-server` |
|
||||||
|
| `app/app/` | 用户端前端(uni-app,基于 unibest) | `iotlcm-app-web` |
|
||||||
|
| `sms/` | 短信服务(HTTP / gRPC) | `iotlcm-sms` |
|
||||||
|
| `bot/` | 飞书通知机器人(运维自用,可选) | `iotlcm-bot` |
|
||||||
|
| `docs/` | 文档站(VitePress)与 OpenAPI 合同 | `iotlcm-docs` |
|
||||||
|
| `onelink-mock/` | OneLink 接口挡板(联调 / 演示,可选) | `iotlcm-onelink-mock` |
|
||||||
|
| `deploy/` | Docker Compose 编排与数据库初始化 | — |
|
||||||
|
|
||||||
|
## 下一步
|
||||||
|
|
||||||
|
- [快速开始](/projects/iotlcm/guide/quickstart) — 三步把整套跑起来
|
||||||
|
- [部署与运维](/projects/iotlcm/guide/deploy) — 生产部署前的必改项、可选组件、离线部署与排错
|
||||||
|
- [教程视频](/projects/iotlcm/guide/tutorial) — 部署与使用的录屏教程
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
title: XiuAdmin
|
||||||
|
description: 基于 GoFrame2 + Vue3 的全栖中后台框架
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# XiuAdmin
|
||||||
|
|
||||||
|
基于全新 GoFrame2 + Vue3 + VueVbenAdmin 开发的全栖框架,适合大型中后台完整应用开发。
|
||||||
|
|
||||||
|
| 项 | 值 |
|
||||||
|
| --- | --- |
|
||||||
|
| 代码仓 | <https://gitee.com/snowgoat/xiu-admin> |
|
||||||
|
| 许可证 | Apache-2.0 |
|
||||||
|
| 状态 | 已发布 |
|
||||||
|
| 技术栈 | GoFrame v2、Vue 3、Vue Vben Admin、Ant Design Vue、asynq |
|
||||||
|
|
||||||
|
## 框架特征
|
||||||
|
|
||||||
|
- 高生产率:极强的可扩展性,应用化、模块化、插件化机制敏捷开发,几分钟即可搭建一个应用开发框架
|
||||||
|
- 快速生成代码:无需编写代码,只需创建表进行简单配置就能生成完善的 CURD、树表等常用开发代码,所需表单控件也是勾选即可直接生成
|
||||||
|
- 认证机制:采用 JWT 的用户状态认证
|
||||||
|
- 路由模式:得益于 GoFrame 提供了规范化的路由注册方式,无需注解自动生成 api 文档
|
||||||
|
- 模块化设计,面向接口开发
|
||||||
|
|
||||||
|
## 内置功能
|
||||||
|
|
||||||
|
1. 租户管理:系统内租户的管理,如租户套餐、过期时间、用户数量、企业信息等
|
||||||
|
2. 租户套餐管理:租户所能使用的套餐管理,如套餐内所包含的菜单等
|
||||||
|
3. 客户端管理:系统内对接的所有客户端管理,如 pc 端、小程序端等
|
||||||
|
4. 用户管理:用户是系统操作者,完成系统用户配置
|
||||||
|
5. 部门管理:配置系统组织机构(公司、部门、岗位),树结构展现支持数据权限
|
||||||
|
6. 岗位管理:配置系统用户所属担任职务
|
||||||
|
7. 菜单管理:配置系统菜单,操作权限、按钮权限标识等
|
||||||
|
8. 角色管理:角色菜单权限分配、设置角色按机构或按上下级关系进行数据范围权限划分
|
||||||
|
9. 字典管理:对系统中经常使用的一些特定数据进行维护,支持枚举字典和自定义方法字典
|
||||||
|
10. 配置管理:对系统动态配置常用参数
|
||||||
|
11. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询
|
||||||
|
12. 登录日志:系统登录日志记录查询,包含登录异常
|
||||||
|
13. 服务日志:服务端运行所产生的警告、异常、崩溃日志的详细数据和堆栈信息
|
||||||
|
14. 在线用户:当前系统中活跃用户状态监控
|
||||||
|
15. 定时任务:在线(添加、修改、删除)任务调度,包含执行结果日志
|
||||||
|
16. 代码生成:支持自动化生成前后端代码。CURD 关联表、树表、消息队列、定时任务一键生成等
|
||||||
|
17. 服务监控:监视当前系统 CPU、内存、磁盘、网络、堆栈等相关信息
|
||||||
|
18. 缓存监控:对系统的缓存信息查询、命令统计等
|
||||||
|
19. 附件管理:文件图片上传,大文件分片上传、断点续传,支持本地
|
||||||
|
20. 消息队列:Asynq 消息队列
|
||||||
|
21. 通知公告:采用 WebSocket 实时推送在线用户最新通知、公告、私信消息
|
||||||
|
22. 常用工具:集成常用的工具包和命令行工具,可以快速开发自定义命令行,多种启动入口
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```text
|
||||||
|
xiu-admin/
|
||||||
|
├── server/ # GoFrame 后端服务
|
||||||
|
├── vue/ # 前端 pnpm monorepo
|
||||||
|
├── docs/ # 文档与演示图例
|
||||||
|
├── build/ # 构建脚本
|
||||||
|
└── deploy/ # 部署配置
|
||||||
|
```
|
||||||
|
|
||||||
|
## 参与贡献
|
||||||
|
|
||||||
|
1. Fork 代码仓并创建特性分支
|
||||||
|
2. 提交前确保 `go build ./...` 与 `pnpm build` 通过
|
||||||
|
3. 提交 Pull Request 并说明变更动机
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
---
|
||||||
|
title: 秀杰 DTU 管理平台
|
||||||
|
description: DTU 设备全生命周期管理平台
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# 秀杰 DTU 管理平台
|
||||||
|
|
||||||
|
面向 DTU(数据传输单元)设备全生命周期的开源管理平台,覆盖设备生产、出厂、代理商划拨、终端客户绑定、配置下发、固件 OTA 升级、指令下发与运行监控的完整闭环。
|
||||||
|
|
||||||
|
| 项 | 值 |
|
||||||
|
| --- | --- |
|
||||||
|
| 代码仓 | <https://gitee.com/snowgoat/xiu-dtu> |
|
||||||
|
| 许可证 | Apache-2.0 |
|
||||||
|
| 状态 | 已发布 |
|
||||||
|
| 技术栈 | GoFrame v2、Vue 3 + Vite + TypeScript、Vben Admin 5.x、Ant Design Vue、MySQL 8、Redis、TDengine(可选)、asynq、VitePress |
|
||||||
|
|
||||||
|
## 功能概览
|
||||||
|
|
||||||
|
| 模块 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| 生产中心 | 产品、硬件型号、工厂、生产批次、设备生产与导入导出 |
|
||||||
|
| 研发中心 | 固件管理、固件版本、OTA 升级任务 |
|
||||||
|
| 配置中心 | 产品通用模型、配置分组、配置版本与下发状态 |
|
||||||
|
| 代理中心 | 代理商设备列表、下级代理、设备划拨与划拨历史、我的订单 |
|
||||||
|
| 系统管理 | 用户、角色、菜单、部门、字典、参数、租户、监控 |
|
||||||
|
| 设备接入 | HTTP / TCP 双通道接入,指令下发、事件上报、UART 隧道 |
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```text
|
||||||
|
.
|
||||||
|
├── server/ # GoFrame 后端服务
|
||||||
|
│ ├── api/ # 接口定义
|
||||||
|
│ ├── internal/ # 业务逻辑
|
||||||
|
│ ├── config/ # 配置文件
|
||||||
|
│ └── storage/ # 数据库初始化脚本
|
||||||
|
├── vue/ # 前端 pnpm monorepo
|
||||||
|
│ └── apps/web-antd/
|
||||||
|
├── docs_ui/ # VitePress 文档站
|
||||||
|
├── docker/ # Docker 部署配置
|
||||||
|
├── deploy/ # systemd / nginx 部署样例
|
||||||
|
└── tools/ # 设备联调与压测脚本
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
环境要求:Go 1.25+、Node.js 24+ 与 pnpm 10+、MySQL 8、Redis 7。
|
||||||
|
|
||||||
|
### 1. 初始化数据库
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mysql -u root -p -e "CREATE DATABASE xiujie_dtu DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
|
||||||
|
mysql -u root -p xiujie_dtu < server/storage/data/xiu_admin.sql
|
||||||
|
mysql -u root -p xiujie_dtu < server/storage/data/xiu_dtu_init.sql
|
||||||
|
mysql -u root -p xiujie_dtu < server/storage/data/xiu_dtu_menu.sql
|
||||||
|
mysql -u root -p xiujie_dtu < server/storage/data/xiu_dtu_roles.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 配置后端
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp server/config/config_default.yaml server/config/config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
按实际环境修改 `config.yaml` 中的数据库、Redis 连接信息。JWT 签名密钥通过环境变量注入,长度不少于 32 位,未提供时服务会拒绝签发与校验令牌。
|
||||||
|
|
||||||
|
### 3. 启动后端
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd server
|
||||||
|
go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
后端默认监听:控制台接口 `:5689`、设备 HTTP 接口 `:5692`、设备 TCP 接入 `:5690`。
|
||||||
|
|
||||||
|
### 4. 启动前端
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd vue
|
||||||
|
pnpm install
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
前端开发地址 `http://localhost:5688`。默认管理员账号 `admin`,首次登录后请立即修改。
|
||||||
|
|
||||||
|
### 5. 启动文档站(可选)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd docs_ui
|
||||||
|
npm install
|
||||||
|
npm run docs:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
文档站地址 `http://localhost:5687`。
|
||||||
|
|
||||||
|
## 设备联调
|
||||||
|
|
||||||
|
`tools/` 下提供设备侧联调与压测脚本:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# HTTP 通道联调
|
||||||
|
BASE_URL="http://127.0.0.1:5692" \
|
||||||
|
DEVICE_KEY="202606270101010001" \
|
||||||
|
PRODUCT_KEY="DTU_TEST" \
|
||||||
|
PROJECT_KEY="PROJECT_KEY_TEST" \
|
||||||
|
./tools/test-device-http-v4.sh
|
||||||
|
|
||||||
|
# TCP 通道联调(含心跳与指令监听)
|
||||||
|
TCP_HOST="127.0.0.1" \
|
||||||
|
DEVICE_KEY="202606270101010001" \
|
||||||
|
PRODUCT_KEY="DTU_TEST" \
|
||||||
|
PROJECT_KEY="PROJECT_KEY_TEST" \
|
||||||
|
python3 tools/test-device-tcp-v4.py --heartbeat --listen-command
|
||||||
|
|
||||||
|
# 2 万设备压测
|
||||||
|
BASE_URL="http://127.0.0.1:5689" TCP_HOST="127.0.0.1" \
|
||||||
|
FACTORY_CODE="TEST_FACTORY_ROLE" PH_CODE="HW_V1" \
|
||||||
|
PRODUCT_KEY="DTU_TEST" PROJECT_KEY="PROJECT_KEY_TEST" \
|
||||||
|
python3 tools/stress-2w-devices.py --count 20000 --device-key-template 'SIM{index:012d}'
|
||||||
|
```
|
||||||
|
|
||||||
|
协议细节见代码仓文档站的「通信协议」章节。
|
||||||
|
|
||||||
|
## Docker 部署
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp docker/dtu_iot/.env.example docker/dtu_iot/.env
|
||||||
|
# 编辑 .env,填入实际域名、数据库与 Redis 口令
|
||||||
|
cd docker/dtu_base && ./deploy.sh # 初始化 MySQL / Redis / TDengine
|
||||||
|
cd ../dtu_iot && ./deploy.sh # 部署业务服务
|
||||||
|
```
|
||||||
|
|
||||||
|
镜像构建与推送:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export DOCKER_USERNAME='your-registry-user'
|
||||||
|
export DOCKER_PASSWORD='your-registry-token'
|
||||||
|
make docker-build docker-push
|
||||||
|
```
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||||
|
<rect width="64" height="64" rx="12" fill="#0f766e"/>
|
||||||
|
<path d="M20 18h24a4 4 0 0 1 4 4v20a4 4 0 0 1-4 4H20a4 4 0 0 1-4-4V22a4 4 0 0 1 4-4z" fill="none" stroke="#fff" stroke-width="3"/>
|
||||||
|
<path d="M24 28h16M24 36h10" stroke="#fff" stroke-width="3" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 344 B |
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* 校验「项目注册表」与「详情页目录」是否一一对应。
|
||||||
|
*
|
||||||
|
* 门户的项目清单由 data/projects.ts 单一驱动。如果注册表加了项目却忘了写详情页,
|
||||||
|
* 或者写了详情页却没登记进注册表,首页卡片与侧边栏就会和实际内容脱节。
|
||||||
|
* 这个脚本在 dev / build 之前自动跑,不一致直接退出非零。
|
||||||
|
*/
|
||||||
|
import { readdirSync, readFileSync, existsSync } from 'node:fs'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { dirname, join } from 'node:path'
|
||||||
|
|
||||||
|
const root = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||||
|
const dataDir = join(root, 'data')
|
||||||
|
const pagesDir = join(root, 'pages', 'projects')
|
||||||
|
|
||||||
|
// 注册表是 TS 源文件,这里只需要 id 列表,正则抽取即可,不引入编译步骤
|
||||||
|
const registrySrc = readdirSync(dataDir)
|
||||||
|
.filter((f) => f.endsWith('.ts'))
|
||||||
|
.map((f) => readFileSync(join(dataDir, f), 'utf-8'))
|
||||||
|
.join('\n')
|
||||||
|
|
||||||
|
const registered = [...registrySrc.matchAll(/\bid:\s*'([^']+)'/g)].map((m) => m[1])
|
||||||
|
|
||||||
|
if (registered.length === 0) {
|
||||||
|
console.error('[check] 注册表里没解析到任何项目,检查 data/projects.ts 的格式')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDisk = existsSync(pagesDir)
|
||||||
|
? readdirSync(pagesDir, { withFileTypes: true })
|
||||||
|
.filter((d) => d.isDirectory())
|
||||||
|
.map((d) => d.name)
|
||||||
|
: []
|
||||||
|
|
||||||
|
const missingPages = registered.filter((id) => !onDisk.includes(id))
|
||||||
|
const missingEntries = onDisk.filter((id) => !registered.includes(id))
|
||||||
|
|
||||||
|
if (missingPages.length || missingEntries.length) {
|
||||||
|
if (missingPages.length) {
|
||||||
|
console.error(`[check] 注册表里有项目但缺详情页:${missingPages.join(', ')}`)
|
||||||
|
}
|
||||||
|
if (missingEntries.length) {
|
||||||
|
console.error(`[check] 有详情页但没登记进注册表:${missingEntries.join(', ')}`)
|
||||||
|
}
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[check] OK:注册表 ${registered.length} 个项目与详情页一一对应(${registered.join(', ')})`)
|
||||||
Reference in New Issue
Block a user