小律书 技术架构详解:前后端分离的自律管理系统设计
目录
- [小律书 技术架构详解:前后端分离的自律管理系统设计](#小律书 技术架构详解:前后端分离的自律管理系统设计)
-
- 一、整体架构概览
-
- [1.1 系统架构图](#1.1 系统架构图)
- [1.2 技术栈矩阵](#1.2 技术栈矩阵)
- 二、前端架构设计
-
- [2.1 目录结构与模块化](#2.1 目录结构与模块化)
- [2.2 网络请求层设计](#2.2 网络请求层设计)
-
- [2.2.1 Request.js 核心实现](#2.2.1 Request.js 核心实现)
- [2.2.2 API 接口层设计](#2.2.2 API 接口层设计)
- [2.3 应用生命周期管理](#2.3 应用生命周期管理)
-
- [2.3.1 App.vue 全局配置](#2.3.1 App.vue 全局配置)
- [2.4 主题系统设计](#2.4 主题系统设计)
- 三、后端架构设计
-
- [3.1 多模块 Maven 项目结构](#3.1 多模块 Maven 项目结构)
- [3.2 LazyBones 核心业务模块](#3.2 LazyBones 核心业务模块)
-
- [3.2.1 领域模型设计](#3.2.1 领域模型设计)
- [3.2.2 Controller 层设计](#3.2.2 Controller 层设计)
- [3.2.3 Service 层设计](#3.2.3 Service 层设计)
- [3.3 数据库设计](#3.3 数据库设计)
-
- [3.3.1 核心表结构](#3.3.1 核心表结构)
- [3.4 安全认证设计](#3.4 安全认证设计)
-
- [3.4.1 Sa-Token 配置](#3.4.1 Sa-Token 配置)
- [3.4.2 JWT Token 流程](#3.4.2 JWT Token 流程)
- 四、数据流与通信机制
-
- [4.1 完整请求流程](#4.1 完整请求流程)
- [4.2 跨页面通信](#4.2 跨页面通信)
- 五、性能优化策略
-
- [5.1 前端优化](#5.1 前端优化)
- [5.2 后端优化](#5.2 后端优化)
- 六、总结与展望
摘要:本文深入剖析 Inner Citadel(内心堡垒)自律管理系统的完整技术架构,涵盖前后端分层设计、微服务模块划分、数据模型设计、API 接口规范以及跨平台通信机制。通过详细的架构图和代码示例,展示如何构建一个企业级、可扩展的移动应用系统。
一、整体架构概览
1.1 系统架构图
┌─────────────────────────────────────────────────────────────┐
│ 用户层 (User Layer) │
├─────────────────┬─────────────────┬─────────────────────────┤
│ 微信小程序 │ H5 页面 │ Android/iOS APP │
│ (WeChat Mini) │ (Mobile Web) │ (Native/Hybrid) │
└────────┬────────┴────────┬────────┴──────────┬──────────────┘
│ │ │
└─────────────────┼────────────────────┘
│
┌────────▼────────┐
│ UniApp 框架 │
│ (跨平台适配层) │
└────────┬────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
┌────────▼────────┐ ┌─────▼──────┐ ┌────────▼────────┐
│ 网络请求层 │ │ 状态管理 │ │ UI 组件库 │
│ Request.js │ │ Vuex/Store │ │ UView Plus │
└────────┬────────┘ └────────────┘ └─────────────────┘
│
│ HTTPS/WebSocket
│
▼
┌─────────────────────────────────────────────────────────────┐
│ API 网关层 (Gateway) │
│ Nginx / Spring Cloud Gateway │
└────────────────────────┬────────────────────────────────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌────────▼────────┐ ┌───▼────────┐ ┌───▼──────────┐
│ 认证授权服务 │ │ 业务服务 │ │ 公共服务 │
│ OAuth Server │ │ LazyBones │ │ System │
└─────────────────┘ └─────┬──────┘ └──────────────┘
│
┌─────────▼──────────┐
│ Spring Boot 2.5 │
│ 应用服务层 │
└─────────┬──────────┘
│
┌────────────────┼────────────────┐
│ │ │
┌────────▼────────┐ ┌────▼──────┐ ┌──────▼────────┐
│ MyBatis Plus │ │ Redis │ │ PostgreSQL │
│ ORM 框架 │ │ 缓存层 │ │ 数据库 │
└─────────────────┘ └───────────┘ └───────────────┘
1.2 技术栈矩阵
| 层级 | 技术选型 | 版本 | 说明 |
|---|---|---|---|
| 前端框架 | UniApp | 3.x | 跨平台开发框架 |
| 前端语言 | Vue.js | 3.x | Composition API |
| UI 组件库 | UView Plus | 3.7+ | 移动端组件库 |
| 网络请求 | uni.request | - | 封装 Axios 类似物 |
| 后端框架 | Spring Boot | 2.5.14 | 快速开发框架 |
| ORM 框架 | MyBatis Plus | 3.5.3.1 | 数据库操作 |
| 数据库 | PostgreSQL | 12+ | 关系型数据库 |
| 缓存 | Redis | 6.x | 分布式缓存 |
| 认证 | Sa-Token | 最新 | 权限认证框架 |
| API 文档 | Swagger 3 | 3.0.0 | 接口文档生成 |
二、前端架构设计
2.1 目录结构与模块化
inner-citadel-app/
├── common/ # 公共模块层
│ ├── api/ # API 接口定义
│ │ ├── auth.js # 认证接口(登录/注册/微信授权)
│ │ └── lazybones.js # 业务接口(任务/监督/统计)
│ ├── config.js # 配置文件(API 基础路径)
│ ├── request.js # 网络请求封装
│ └── share.js # 分享功能混入
├── pages/ # 页面文件层
│ ├── home/ # 首页(任务看板)
│ ├── task/ # 任务管理
│ │ ├── create.vue # 创建任务
│ │ ├── detail.vue # 任务详情
│ │ ├── records.vue # 任务清单
│ │ ├── supervised.vue # 我监督的任务
│ │ ├── assigned.vue # 指派给我的任务
│ │ └── overdue.vue # 超时任务
│ ├── auth/ # 认证模块
│ │ └── login.vue # 登录/注册页
│ ├── stats/ # 数据统计
│ ├── profile/ # 个人中心
│ └── settings/ # 设置页面
├── static/ # 静态资源
│ ├── images/ # 图片资源
│ └── css/ # 全局样式
├── App.vue # 应用配置(生命周期/主题)
├── main.js # 入口文件
├── pages.json # 页面路由配置
├── manifest.json # 应用配置
└── uni.scss # 全局样式变量
2.2 网络请求层设计
2.2.1 Request.js 核心实现
javascript
// common/request.js
const isAbsoluteUrl = (url) => /^https?:\/\//i.test(String(url || ''));
const joinUrl = (base, path) => {
const p = String(path || '').trim();
if (!p) return String(base || '');
if (isAbsoluteUrl(p)) return p;
const b = String(base || '').replace(/\/+$/, '');
if (!b) return p;
if (p.startsWith(b)) return p;
if (p.startsWith('/')) return b + p;
return b + '/' + p;
};
const request = (options) => {
return new Promise((resolve, reject) => {
// 1. 获取 Token
const token = uni.getStorageSync('token');
// 2. 构建请求头
const header = {
'Content-Type': 'application/json',
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN',
...options.header
};
// 3. JWT Token 注入
if (token) {
header['Authorization'] = 'Bearer ' + token;
}
// 4. URL 拼接
const fullUrl = joinUrl(baseUrl, options.url);
const custom = (options.custom || {});
const timeout = typeof options.timeout === 'number' ? options.timeout : 15000;
// 5. 发起请求
uni.request({
url: fullUrl,
method: options.method || 'GET',
data: options.data || {},
header: header,
timeout,
success: (res) => {
// 6. 响应处理
if (res.statusCode >= 200 && res.statusCode < 300) {
const data = res.data;
if (data && typeof data === 'object' && data.hasOwnProperty('code')) {
if (data.code === 200 || data.code === 0) {
resolve(data.hasOwnProperty('rows') ? data : data.data || data);
} else {
// 业务错误处理
if (data.code === 401) {
// Token 过期,跳转登录
redirectToLogin();
}
reject(data);
}
} else {
resolve(data);
}
} else if (res.statusCode === 401) {
// HTTP 401 未授权
redirectToLogin();
reject(res.data);
} else {
reject(res.data);
}
},
fail: (err) => {
console.error('Request failed:', err);
uni.showToast({ title: '网络请求失败', icon: 'none' });
reject(err);
}
});
});
};
export default {
get: (url, data, options = {}) => request({ url, method: 'GET', data, ...options }),
post: (url, data, options = {}) => request({ url, method: 'POST', data, ...options }),
put: (url, data, options = {}) => request({ url, method: 'PUT', data, ...options }),
delete: (url, data, options = {}) => request({ url, method: 'DELETE', data, ...options }),
};
2.2.2 API 接口层设计
采用 Repository 模式,将 API 按业务模块分类:
javascript
// common/api/lazybones.js
import request from '../request.js';
// ========== 任务管理 ==========
export const createTask = (data) =>
request.post('/api/lazybones/task/create', data);
export const updateTask = (data) =>
request.put('/api/lazybones/task/update', data);
export const deleteTask = (id) =>
request.delete(`/api/lazybones/task/delete/${encodeURIComponent(id)}`);
export const getTaskDetail = (id, options) =>
request.get(`/api/lazybones/task/detail/${encodeURIComponent(id)}`, null, options);
export const listTasks = (params, options) =>
request.get('/api/lazybones/task/list', params, options);
export const listActiveTasks = (options) =>
request.get('/api/lazybones/task/active', null, options);
// ========== 任务分配 ==========
export const listSupervisedTasks = (options) =>
request.get('/api/lazybones/task/supervised', null, options);
export const listAssignedTasks = (options) =>
request.get('/api/lazybones/task/assigned', null, options);
export const acceptTask = (id) =>
request.post(`/api/lazybones/task/${encodeURIComponent(id)}/accept`);
export const rejectTask = (id) =>
request.post(`/api/lazybones/task/${encodeURIComponent(id)}/reject`);
// ========== 提醒功能 ==========
export const remindTask = (id, data) =>
request.post(`/api/lazybones/task/${encodeURIComponent(id)}/remind`, data);
export const getTaskRemindSummary = (id, options) =>
request.get(`/api/lazybones/task/${encodeURIComponent(id)}/remind/summary`, null, options);
// ========== 监督人管理 ==========
export const addSupervisor = (data) =>
request.post('/api/lazybones/supervisor/add', data);
export const removeSupervisor = (id) =>
request.delete(`/api/lazybones/supervisor/remove/${encodeURIComponent(id)}`);
export const listSupervisors = (options) =>
request.get('/api/lazybones/supervisor/list', null, options);
// ========== 违规记录 ==========
export const createViolation = (data) =>
request.post('/api/lazybones/violation/create', data);
export const listViolations = (options) =>
request.get('/api/lazybones/violation/list', null, options);
// ========== 统计数据 ==========
export const getUserStats = (options) =>
request.get('/api/lazybones/user/stats', null, options);
export const getTaskStats = (params, options) =>
request.get('/api/lazybones/task/stats', params, options);
2.3 应用生命周期管理
2.3.1 App.vue 全局配置
vue
<script>
import { getLoginUserInfo, wechatLogin } from './common/api/auth.js';
export default {
onLaunch: async function() {
// 1. 应用启动时初始化主题
this.applyTheme();
// 2. 监听全局主题切换事件
uni.$on('theme:changed', this.handleGlobalThemeChange);
// 3. 监听系统暗黑模式变化(H5 端)
if (typeof window !== 'undefined' && window.matchMedia) {
this.__sysDarkMM = window.matchMedia('(prefers-color-scheme: dark)');
this.__sysDarkMM.addEventListener('change', () => {
const pref = uni.getStorageSync('modePreference') || '';
if (pref === 'auto') this.applyTheme();
});
}
// 4. 确保登录状态
await this.ensureLogin();
},
onShow: async function() {
// 每次显示时刷新主题和登录状态
this.applyTheme();
await this.ensureLogin();
},
onHide: function() {
console.log('App Hide');
},
methods: {
// 智能登录逻辑
async ensureLogin() {
try {
let token = uni.getStorageSync('token') || '';
if (token) {
// 验证 Token 有效性
try {
const userInfo = await getLoginUserInfo({
custom: { loginRedirect: false }
});
if (userInfo) {
uni.setStorageSync('loginUserInfo', userInfo);
uni.setStorageSync('userInfo', userInfo);
}
return;
} catch (e) {
// Token 失效,清除缓存
uni.removeStorageSync('token');
uni.removeStorageSync('userInfo');
token = '';
}
}
// 微信静默登录
if (typeof uni.login === 'function') {
const code = await new Promise((resolve) => {
uni.login({
success: (res) => resolve(res.code),
fail: () => resolve('')
});
});
if (code) {
const ret = await wechatLogin({ code });
if (ret && ret.ok) {
const auth = ret.data || {};
const token = auth.token || auth.userInfo?.accessToken;
if (token) {
uni.setStorageSync('token', token);
uni.setStorageSync('wechatAuth', auth);
// 获取用户信息
const userInfo = await getLoginUserInfo({
custom: { loginRedirect: false }
});
if (userInfo) {
uni.setStorageSync('loginUserInfo', userInfo);
uni.setStorageSync('userInfo', userInfo);
}
}
}
}
}
} catch (e) {
console.error('Auto-login failed:', e);
}
},
// 主题应用逻辑
applyTheme(theme) {
let t = theme || '';
const pref = uni.getStorageSync('modePreference') || '';
if (pref === 'auto') {
t = this.getSystemPreferredTheme();
} else if (pref === 'day') {
t = 'light';
} else if (pref === 'night') {
t = 'dark';
} else {
t = t || uni.getStorageSync('theme') || 'light';
}
// 设置根元素类名
this.setRootThemeClass(t);
// 更新 TabBar 样式
this.updateTabBarByTheme(t);
// 触发全局事件
uni.$emit('theme:changed', t);
}
}
}
</script>
2.4 主题系统设计
支持 8 种预设主题的动态切换系统:
scss
// App.vue 样式部分
/* ========== 主题基色定义 ========== */
.theme-light page {
background-color: #F6F7F9;
color: #0B1220;
}
.theme-dark page {
background-color: #F0F2F5;
color: #001529;
}
.theme-neon page {
background-color: #0A0F1A;
color: #E6F7FF;
}
.theme-nord page {
background-color: #2E3440;
color: #ECEFF4;
}
.theme-paper page {
background-color: #FAFAFC;
color: #0B1220;
}
.theme-mint page {
background-color: #F4FBF7;
color: #0B1220;
}
.theme-sepia page {
background-color: #F7F1E3;
color: #3E3A33;
}
.theme-flow page {
background-color: #F0F2F5;
color: #0B1220;
}
/* ========== 主题变量映射 ========== */
.theme-light .bg-card { background-color: #FFFFFF; }
.theme-light .text-white { color: #0B1220; }
.theme-light .border-slate-700 { border: 1px solid #E5E7EB; }
.theme-dark .bg-card { background-color: #FFFFFF; }
.theme-dark .text-white { color: #001529; }
.theme-dark .border-slate-700 { border: 1px solid #e2e8f0; }
/* ========== TabBar 主题适配 ========== */
updateTabBarByTheme(t) {
const themeMap = {
dark: {
backgroundColor: '#FFFFFF',
selectedColor: '#10b981'
},
light: {
backgroundColor: '#FFFFFF',
selectedColor: '#10b981'
},
neon: {
backgroundColor: '#FFFFFF',
selectedColor: '#22D3EE'
}
};
const s = themeMap[t] || themeMap.dark;
uni.setTabBarStyle({
backgroundColor: s.backgroundColor,
selectedColor: s.selectedColor
});
}
三、后端架构设计
3.1 多模块 Maven 项目结构
huimayun-boot/
├── huimayun-webservice/ # Web 服务启动模块
│ └── src/main/
│ ├── java/com/huimayun/ # 启动类
│ └── resources/ # 配置文件
│ ├── application.yml # 主配置
│ └── application-dev.yml # 开发环境
├── huimayun-common/ # 通用工具模块
│ └── src/main/java/com/huimayun/common/
│ ├── annotation/ # 自定义注解
│ ├── config/ # 配置类
│ ├── constant/ # 常量定义
│ ├── core/ # 核心工具
│ ├── enums/ # 枚举类
│ ├── exception/ # 异常处理
│ ├── filter/ # 过滤器
│ ├── utils/ # 工具类
│ └── web/ # Web 相关
├── huimayun-framework/ # 核心框架模块
│ └── src/main/java/com/huimayun/framework/
│ ├── config/ # 框架配置
│ ├── datasource/ # 数据源配置
│ ├── redis/ # Redis 配置
│ └── web/ # Web 配置
├── huimayun-boot-system/ # 系统管理模块
│ └── src/main/
│ ├── java/com/huimayun/ # 系统业务代码
│ └── resources/mybatis/ # MyBatis XML
├── huimayun-boot-module/ # 业务模块聚合
│ ├── huimayun-module-lazybones/ # 懒虫管理(核心业务)
│ ├── huimayun-module-asset/ # 资产管理
│ ├── huimayun-module-campus/ # 校园卡管理
│ └── huimayun-module-quartz/ # 定时任务
└── huimayun-boot-oauth/ # OAuth 认证模块
3.2 LazyBones 核心业务模块
3.2.1 领域模型设计
java
// LbTask.java - 任务实体
package com.huimayun.lazybones.domain;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class LbTask implements Serializable {
private static final long serialVersionUID = 1L;
// 基础信息
private String id; // 任务 ID(UUID)
private String userId; // 创建者 ID
private String title; // 任务标题
private String description; // 任务描述
// 时间管理
private LocalDateTime deadline; // 截止时间
private Integer defconLevel; // 优先级等级 (1-4)
// 进度管理
private Integer progress; // 进度百分比 (0-100)
private String status; // ACTIVE, COMPLETED, FAILED, DELAYED
// 任务类型
private String type; // survival/work/word:multi/single
private String contractTerms; // 契约条款
private String punishmentConfig; // 惩罚配置
// 社交属性
private String supervisorIds; // 监督人 ID 列表
private String visibility; // PUBLIC, PRIVATE
private String assigneeIds; // 被指派人 ID 列表
private String assignStatus; // PENDING, ACCEPTED, REJECTED
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// 扩展信息(VO 字段)
private String creatorName;
private String creatorPhone;
}
java
// LbSupervisor.java - 监督人实体
package com.huimayun.lazybones.domain;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class LbSupervisor implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String userId; // 任务创建者 ID
private String supervisorId; // 监督人 ID
private String supervisorName; // 监督人姓名
private String supervisorPhone; // 监督人手机号
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
java
// LbViolationLog.java - 违规记录
package com.huimayun.lazybones.domain;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class LbViolationLog implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String taskId; // 关联任务 ID
private String userId; // 用户 ID
private String violationType; // 违规类型
private String description; // 描述
private LocalDateTime violationTime; // 违规时间
private LocalDateTime createdAt;
}
3.2.2 Controller 层设计
采用 RESTful 风格,统一响应格式:
java
// LbTaskController.java
package com.huimayun.lazybones.controller;
import com.huimayun.common.utils.SecurityUtils;
import com.huimayun.common.web.domain.UserInfoVo;
import com.huimayun.lazybones.domain.LbTask;
import com.huimayun.lazybones.service.LbTaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/lazybones/task")
public class LbTaskController {
@Autowired
private LbTaskService taskService;
/**
* 创建任务
* POST /api/lazybones/task/create
*/
@PostMapping("/create")
public ResponseEntity<LbTask> createTask(@RequestBody LbTask task) {
String userId = getLoginUserId();
if (userId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
task.setUserId(userId);
return ResponseEntity.ok(taskService.createTask(task));
}
/**
* 更新任务
* PUT /api/lazybones/task/update
*/
@PutMapping("/update")
public ResponseEntity<LbTask> updateTask(@RequestBody LbTask task) {
String userId = getLoginUserId();
if (userId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
task.setUserId(userId);
return ResponseEntity.ok(taskService.updateTask(task));
}
/**
* 删除任务
* DELETE /api/lazybones/task/delete/{id}
*/
@DeleteMapping("/delete/{id}")
public ResponseEntity<Void> deleteTask(@PathVariable String id) {
taskService.deleteTask(id);
return ResponseEntity.ok().build();
}
/**
* 获取任务详情
* GET /api/lazybones/task/detail/{id}
*/
@GetMapping("/detail/{id}")
public ResponseEntity<LbTask> getTask(@PathVariable String id) {
return ResponseEntity.ok(taskService.getTaskById(id));
}
/**
* 获取所有任务列表
* GET /api/lazybones/task/list
*/
@GetMapping("/list")
public ResponseEntity<List<LbTask>> getTasks() {
String userId = getLoginUserId();
if (userId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return ResponseEntity.ok(taskService.getTasksByUserId(userId));
}
/**
* 获取进行中的任务列表
* GET /api/lazybones/task/active
*/
@GetMapping("/active")
public ResponseEntity<List<LbTask>> getActiveTasks() {
String userId = getLoginUserId();
if (userId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return ResponseEntity.ok(taskService.getActiveTasks(userId));
}
/**
* 获取我监督的任务
* GET /api/lazybones/task/supervised
*/
@GetMapping("/supervised")
public ResponseEntity<List<LbTask>> getSupervisedTasks() {
String userId = getLoginUserId();
if (userId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return ResponseEntity.ok(taskService.getSupervisedTasks(userId));
}
/**
* 获取指派给我的任务
* GET /api/lazybones/task/assigned
*/
@GetMapping("/assigned")
public ResponseEntity<List<LbTask>> getAssignedTasks() {
String userId = getLoginUserId();
if (userId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return ResponseEntity.ok(taskService.getAssignedTasks(userId));
}
/**
* 接受任务
* POST /api/lazybones/task/{id}/accept
*/
@PostMapping("/{id}/accept")
public ResponseEntity<Void> acceptTask(@PathVariable String id) {
String userId = getLoginUserId();
if (userId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
taskService.acceptTask(id, userId);
return ResponseEntity.ok().build();
}
/**
* 拒绝任务
* POST /api/lazybones/task/{id}/reject
*/
@PostMapping("/{id}/reject")
public ResponseEntity<Void> rejectTask(@PathVariable String id) {
String userId = getLoginUserId();
if (userId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
taskService.rejectTask(id, userId);
return ResponseEntity.ok().build();
}
/**
* 提醒任务
* POST /api/lazybones/task/{id}/remind
*/
@PostMapping("/{id}/remind")
public ResponseEntity<Void> remindTask(
@PathVariable String id,
@RequestBody(required = false) Map<String, Object> body) {
String userId = getLoginUserId();
if (userId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
String remindType = body != null && body.get("type") != null
? String.valueOf(body.get("type")).trim()
: "durian";
taskService.remindTask(id, userId, remindType);
return ResponseEntity.ok().build();
}
/**
* 获取任务统计数据
* GET /api/lazybones/task/stats
*/
@GetMapping("/stats")
public ResponseEntity<LbTaskStatsVo> getTaskStats(
@RequestParam(required = false) String range) {
String userId = getLoginUserId();
if (userId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return ResponseEntity.ok(taskService.getTaskStats(userId, range));
}
/**
* 获取当前登录用户 ID
*/
private String getLoginUserId() {
UserInfoVo user = SecurityUtils.getLoginUser();
if (user == null) return null;
String id = user.getId();
if (id == null || id.trim().isEmpty()) return null;
return id.trim();
}
}
3.2.3 Service 层设计
java
// LbTaskService.java - 业务逻辑接口
package com.huimayun.lazybones.service;
import com.huimayun.lazybones.domain.LbTask;
import com.huimayun.lazybones.domain.LbTaskRemindSummary;
import com.huimayun.lazybones.domain.LbTaskStatsVo;
import java.util.List;
public interface LbTaskService {
/**
* 创建任务
*/
LbTask createTask(LbTask task);
/**
* 更新任务
*/
LbTask updateTask(LbTask task);
/**
* 删除任务
*/
void deleteTask(String id);
/**
* 根据 ID 获取任务
*/
LbTask getTaskById(String id);
/**
* 根据用户 ID 获取任务列表
*/
List<LbTask> getTasksByUserId(String userId);
/**
* 获取进行中的任务
*/
List<LbTask> getActiveTasks(String userId);
/**
* 获取我监督的任务
*/
List<LbTask> getSupervisedTasks(String userId);
/**
* 获取指派给我的任务
*/
List<LbTask> getAssignedTasks(String userId);
/**
* 接受任务
*/
void acceptTask(String id, String userId);
/**
* 拒绝任务
*/
void rejectTask(String id, String userId);
/**
* 提醒任务
*/
void remindTask(String id, String userId, String remindType);
/**
* 获取提醒汇总
*/
LbTaskRemindSummary getRemindSummary(String id, String userId);
/**
* 获取任务统计数据
*/
LbTaskStatsVo getTaskStats(String userId, String range);
}
3.3 数据库设计
3.3.1 核心表结构
sql
-- 任务表
CREATE TABLE lb_task (
id VARCHAR(64) PRIMARY KEY,
user_id VARCHAR(64) NOT NULL COMMENT '创建者 ID',
title VARCHAR(200) NOT NULL COMMENT '任务标题',
description TEXT COMMENT '任务描述',
deadline TIMESTAMP NOT NULL COMMENT '截止时间',
defcon_level INTEGER DEFAULT 4 COMMENT '优先级 (1-4)',
progress INTEGER DEFAULT 0 COMMENT '进度百分比',
status VARCHAR(20) DEFAULT 'ACTIVE' COMMENT '状态',
type VARCHAR(50) COMMENT '任务类型',
contract_terms TEXT COMMENT '契约条款',
punishment_config TEXT COMMENT '惩罚配置',
supervisor_ids TEXT COMMENT '监督人 ID 列表',
visibility VARCHAR(20) DEFAULT 'PRIVATE' COMMENT '可见性',
assignee_ids TEXT COMMENT '被指派人 ID 列表',
assign_status VARCHAR(20) DEFAULT 'PENDING' COMMENT '指派状态',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_id (user_id),
INDEX idx_status (status),
INDEX idx_deadline (deadline)
);
-- 监督人表
CREATE TABLE lb_supervisor (
id VARCHAR(64) PRIMARY KEY,
user_id VARCHAR(64) NOT NULL COMMENT '用户 ID',
supervisor_id VARCHAR(64) NOT NULL COMMENT '监督人 ID',
supervisor_name VARCHAR(100) COMMENT '监督人姓名',
supervisor_phone VARCHAR(20) COMMENT '监督人手机号',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_id (user_id),
INDEX idx_supervisor_id (supervisor_id)
);
-- 违规记录表
CREATE TABLE lb_violation_log (
id VARCHAR(64) PRIMARY KEY,
task_id VARCHAR(64) NOT NULL COMMENT '任务 ID',
user_id VARCHAR(64) NOT NULL COMMENT '用户 ID',
violation_type VARCHAR(50) COMMENT '违规类型',
description TEXT COMMENT '描述',
violation_time TIMESTAMP COMMENT '违规时间',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_task_id (task_id),
INDEX idx_user_id (user_id)
);
-- 任务提醒表
CREATE TABLE lb_task_remind (
id VARCHAR(64) PRIMARY KEY,
task_id VARCHAR(64) NOT NULL COMMENT '任务 ID',
user_id VARCHAR(64) NOT NULL COMMENT '用户 ID',
remind_type VARCHAR(50) COMMENT '提醒类型',
remind_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '提醒时间',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_task_id (task_id),
INDEX idx_user_id (user_id)
);
3.4 安全认证设计
3.4.1 Sa-Token 配置
yaml
# application.yml
sa-token:
# token 名称
token-name: Authorization
# token 前缀
token-prefix: Bearer
# 是否从 cookie 读取
is-read-cookie: false
# token 有效期(30 天)
timeout: 2592000
# 最低活跃频率(不限制)
active-timeout: -1
# 允许多地同时登录
is-concurrent: true
# 共享 token
is-share: true
# token 风格
token-style: uuid
# 输出操作日志
is-log: true
# OAuth2 配置
oauth2:
is-code: true # 授权码模式
is-implicit: false # 关闭隐式模式
is-password: true # 密码模式
is-client: true # 客户端模式
3.4.2 JWT Token 流程
┌──────────┐
│ 用户 │
└────┬─────┘
│ 1. 登录请求(用户名密码/微信 code)
▼
┌─────────────────┐
│ AuthController │
└────┬────────────┘
│ 2. 验证凭证
▼
┌─────────────────┐
│ UserService │
└────┬────────────┘
│ 3. 生成 Token
▼
┌─────────────────┐
│ StpUtil.login()│
└────┬────────────┘
│ 4. 返回 Token
▼
┌──────────┐
│ 用户 │─── 后续请求携带 Bearer Token
└──────────┘
四、数据流与通信机制
4.1 完整请求流程
用户操作 → UniApp → uni.request → Request.js → API 层
↓
HTTPS 请求
↓
Nginx 反向代理
↓
Spring Boot Controller
↓
Service 业务逻辑
↓
MyBatis Plus Mapper
↓
PostgreSQL/Redis
↓
返回 JSON 数据
↓
Request.js 处理
↓
更新 UI 状态
4.2 跨页面通信
使用 UniApp 的事件总线实现跨页面通信:
javascript
// App.vue - 主题切换事件广播
applyTheme(theme) {
// ...
uni.$emit('theme:changed', theme);
}
// pages/home/index.vue - 监听主题变化
export default {
onLoad() {
uni.$on('theme:changed', this.handleThemeChange);
},
onUnload() {
uni.$off('theme:changed', this.handleThemeChange);
},
methods: {
handleThemeChange(theme) {
this.theme = theme;
}
}
}
五、性能优化策略
5.1 前端优化
- 分包加载
json
// manifest.json
"mp-weixin": {
"optimization": {
"subPackages": true
}
}
- 定时器管理
javascript
// 避免内存泄漏
onShow() {
this.startTicker();
}
onHide() {
this.stopTicker();
}
- 条件渲染优化
vue
<view v-if="!hasActiveTasks">
<!-- 空状态 -->
</view>
<view v-else>
<!-- 任务列表 -->
</view>
5.2 后端优化
- Redis 缓存
java
// 热点数据缓存
@Cacheable(value = "task::stats", key = "#userId + '::' + #range")
public LbTaskStatsVo getTaskStats(String userId, String range) {
return taskMapper.selectStats(userId, range);
}
- 数据库索引
sql
-- 常用查询字段建立索引
CREATE INDEX idx_task_user_status ON lb_task(user_id, status);
CREATE INDEX idx_task_deadline ON lb_task(deadline);
- 分页查询
java
// PageHelper 分页
PageHelper.startPage(pageNum, pageSize);
List<LbTask> tasks = taskMapper.selectByUserId(userId);
PageInfo<LbTask> pageInfo = new PageInfo<>(tasks);
六、总结与展望
Inner Citadel 项目展示了一个完整的前后端分离架构设计,具有以下特点:
- 跨平台适配:UniApp 实现一套代码多端运行
- 模块化设计:Maven 多模块清晰划分业务边界
- 安全性:JWT + Sa-Token 双重保障
- 可扩展性:MyBatis Plus 简化数据操作
- 用户体验:8 种主题、流畅动画、实时反馈
未来改进方向:
- 引入 WebSocket 实现实时推送
- 集成 ECharts 增强数据可视化
- 添加 AI 智能推荐算法
- 支持离线模式和 PWA
作者简介:资深全栈架构师,专注于企业级应用架构设计与性能优化。

