master
mula 2025-04-09 08:45:38 +00:00
parent e900da1c54
commit ce3e802edc
59 changed files with 2636 additions and 0 deletions

7
.vscode/extensions.json vendored 100755
View File

@ -0,0 +1,7 @@
{
"recommendations": [
"pomdtr.excalidraw-editor",
"editorconfig.editorconfig",
"lokalise.i18n-ally"
]
}

View File

@ -0,0 +1,16 @@
server:
addr: ":8080"
mode: "debug"
database:
driver: "mysql"
host: "unis-sip-mysql.ns-dv9ov434.svc"
port: 3306
username: "root"
password: "5tzz94jd"
dbname: "unis_sip"
charset: "utf8mb4"
jwt:
secret: "unis-sip-secret-key"
expiretime: 86400 # 24小时

172
db-init.sql 100644
View File

@ -0,0 +1,172 @@
-- 创建系统用户表
CREATE TABLE sys_user (
user_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键用户ID',
dept_id BIGINT(20) DEFAULT NULL COMMENT '部门ID',
login_name VARCHAR(30) NOT NULL COMMENT '登录账号',
user_name VARCHAR(30) DEFAULT '' COMMENT '用户昵称',
email VARCHAR(50) DEFAULT '' COMMENT '用户邮箱',
phonenumber VARCHAR(11) DEFAULT '' COMMENT '手机号码',
sex CHAR(1) DEFAULT '0' COMMENT '用户性别0男 1女 2未知',
avatar VARCHAR(100) DEFAULT '' COMMENT '头像路径',
password VARCHAR(50) DEFAULT '' COMMENT '密码',
salt VARCHAR(20) DEFAULT '' COMMENT '盐加密',
status CHAR(1) DEFAULT '0' COMMENT '帐号状态0正常 1停用',
login_ip VARCHAR(128) DEFAULT '' COMMENT '最后登录IP',
login_date DATETIME DEFAULT NULL COMMENT '最后登录时间',
create_at DATETIME NOT NULL COMMENT '创建时间',
update_at DATETIME NOT NULL COMMENT '更新时间',
delete_at DATETIME DEFAULT NULL COMMENT '删除时间,软删除',
PRIMARY KEY (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表';
-- 创建系统角色表
CREATE TABLE sys_role (
role_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键角色ID',
role_name VARCHAR(30) NOT NULL COMMENT '角色名称',
role_key VARCHAR(100) NOT NULL COMMENT '角色权限字符串',
data_scope CHAR(1) DEFAULT '1' COMMENT '数据范围1全部数据权限 2自定数据权限 3本部门数据权限 4本部门及以下数据权限',
status CHAR(1) NOT NULL COMMENT '角色状态0正常 1停用',
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
delete_at DATETIME DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (role_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统角色表';
-- 初始化角色数据
INSERT INTO sys_role (role_id, role_name, role_key, data_scope, status, create_time) VALUES
(1, '超级管理员', 'admin', '1', '0', NOW()),
(2, '普通角色', 'common', '2', '0', NOW());
-- 初始化用户数据
INSERT INTO sys_user (user_id, login_name, user_name, password, salt, status, create_at, update_at) VALUES
(1, 'admin', 'admin', '123456', 'salt', '0', NOW(), NOW()),
(2, 'mula', 'mula', '123456', 'salt', '0', NOW(), NOW()),
(3, 'jsm', 'jsm', '123456', 'salt', '0', NOW(), NOW());
-- 创建用户角色关系表
CREATE TABLE sys_user_role (
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
user_id BIGINT(20) NOT NULL COMMENT '用户ID',
role_id BIGINT(20) NOT NULL COMMENT '角色ID',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关系表';
-- 初始化用户角色关联关系
INSERT INTO sys_user_role (user_id, role_id) VALUES
(1, 1), -- admin用户关联超级管理员角色
(2, 2), -- mula用户关联普通角色
(3, 2); -- jsm用户关联普通角色
-- 创建部门表
CREATE TABLE sys_dept (
dept_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '部门id',
parent_id BIGINT(20) DEFAULT 0 COMMENT '父部门id',
dept_name VARCHAR(30) DEFAULT '' COMMENT '部门名称',
order_num INT(4) DEFAULT 0 COMMENT '显示顺序',
leader VARCHAR(20) DEFAULT NULL COMMENT '负责人',
phone VARCHAR(11) DEFAULT NULL COMMENT '联系电话',
email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
status CHAR(1) DEFAULT '0' COMMENT '部门状态0正常 1停用',
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
delete_at DATETIME DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (dept_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='部门表';
-- 创建菜单权限表
CREATE TABLE sys_menu (
menu_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '菜单ID',
menu_name VARCHAR(50) NOT NULL COMMENT '菜单名称',
parent_id BIGINT(20) DEFAULT 0 COMMENT '父菜单ID',
order_num INT(4) DEFAULT 0 COMMENT '显示顺序',
path VARCHAR(200) DEFAULT '' COMMENT '路由地址',
component VARCHAR(255) DEFAULT NULL COMMENT '组件路径',
is_frame INT(1) DEFAULT 1 COMMENT '是否为外链0是 1否',
menu_type CHAR(1) DEFAULT '' COMMENT '菜单类型M目录 C菜单 F按钮',
visible CHAR(1) DEFAULT '0' COMMENT '菜单状态0显示 1隐藏',
perms VARCHAR(100) DEFAULT NULL COMMENT '权限标识',
icon VARCHAR(100) DEFAULT '#' COMMENT '菜单图标',
status CHAR(1) DEFAULT '0' COMMENT '菜单状态0正常 1停用',
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
delete_at DATETIME DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (menu_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='菜单权限表';
-- 创建角色菜单关系表
CREATE TABLE sys_role_menu (
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
role_id BIGINT(20) NOT NULL COMMENT '角色ID',
menu_id BIGINT(20) NOT NULL COMMENT '菜单ID',
PRIMARY KEY(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色和菜单关系表';
-- 初始化部门数据
INSERT INTO sys_dept (dept_id, parent_id, dept_name, order_num, create_time) VALUES
(1, 0, '紫光汇智', 1, NOW()),
(2, 1, 'VDI事业部门', 1, NOW()),
(3, 1, '营销中心', 2, NOW()),
(4, 1, '产品中心', 3, NOW());
-- 初始化菜单数据
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, menu_type, visible, perms, icon, create_time) VALUES
-- 一级菜单
(1, '系统管理', 0, 1, 'system', NULL, 'M', '0', '', 'system', NOW()),
(2, '项目管理', 0, 2, 'project', NULL, 'M', '0', '', 'project', NOW()),
-- 系统管理子菜单
(100, '用户管理', 1, 1, 'user', 'system/user/index', 'C', '0', 'system:user:list', 'user', NOW()),
(101, '角色管理', 1, 2, 'role', 'system/role/index', 'C', '0', 'system:role:list', 'role', NOW()),
(102, '菜单管理', 1, 3, 'menu', 'system/menu/index', 'C', '0', 'system:menu:list', 'menu', NOW()),
-- 项目管理子菜单
(200, '项目信息管理', 2, 1, 'info', 'project/info/index', 'C', '0', 'project:info:list', 'info', NOW()),
(201, '合同信息管理', 2, 2, 'contract', 'project/contract/index', 'C', '0', 'project:contract:list', 'contract', NOW());
-- 初始化角色菜单关系
INSERT INTO sys_role_menu (role_id, menu_id) VALUES
-- admin角色系统管理权限
(1, 1), (1, 100), (1, 101), (1, 102),
-- common角色项目管理权限
(2, 2), (2, 200), (2, 201);
-- 创建字典类型表
CREATE TABLE sys_dict_type (
dict_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '字典主键',
dict_name VARCHAR(100) DEFAULT '' COMMENT '字典名称',
dict_type VARCHAR(100) DEFAULT '' COMMENT '字典类型',
status CHAR(1) DEFAULT '0' COMMENT '状态0正常 1停用',
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
delete_at DATETIME DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (dict_id),
UNIQUE KEY dict_type (dict_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典类型表';
-- 创建字典数据表
CREATE TABLE sys_dict_data (
dict_code BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '字典编码',
dict_sort INT(4) DEFAULT 0 COMMENT '字典排序',
dict_label VARCHAR(100) DEFAULT '' COMMENT '字典标签',
dict_value VARCHAR(100) DEFAULT '' COMMENT '字典键值',
dict_type VARCHAR(100) DEFAULT '' COMMENT '字典类型',
status CHAR(1) DEFAULT '0' COMMENT '状态0正常 1停用',
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
delete_at DATETIME DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (dict_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典数据表';
-- 初始化字典类型数据
INSERT INTO sys_dict_type (dict_id, dict_name, dict_type, status, create_time) VALUES
(1, '用户性别', 'sys_user_sex', '0', NOW()),
(2, '合同类别', 'contract_type', '0', NOW());
-- 初始化字典数据
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_time) VALUES
-- 用户性别数据
(1, '', '0', 'sys_user_sex', '0', NOW()),
(2, '', '1', 'sys_user_sex', '0', NOW()),
(3, '未知', '2', 'sys_user_sex', '0', NOW()),
-- 合同类别数据
(1, '直签', '1', 'contract_type', '0', NOW()),
(2, '代理商签', '2', 'contract_type', '0', NOW());

1
entrypoint.sh 100755
View File

@ -0,0 +1 @@
./hello.sh

5
hello.sh 100755
View File

@ -0,0 +1,5 @@
#!/bin/bash
while :; do
{ echo -ne "HTTP/1.1 200 OK\r\nContent-Length: $(echo -n "Hello, World!")\r\n\r\nHello, World!"; } | nc -l -p 8080 -q 1
done

100
pom.xml 100644
View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<groupId>com.unissense.sip</groupId>
<artifactId>unis-sip</artifactId>
<version>1.0.0</version>
<name>unis-sip</name>
<description>Unis Service Information Platform</description>
<properties>
<java.version>11</java.version>
<mybatis-plus.version>3.5.2</mybatis-plus.version>
<mysql.version>8.0.31</mysql.version>
<jwt.version>0.9.1</jwt.version>
<lombok.version>1.18.24</lombok.version>
</properties>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

312
project.md 100644
View File

@ -0,0 +1,312 @@
> SIP是售后服务平台Service Information Platform
# 系统架构
后端采用Spring Boot+JWT+Mybatis-Plus+Redis+Mysql
前端采用vue3 + element-plus + ts
# 数据库设计
采用mysql数据库采用ORM框架进行数据库操作。数据库版本为8.0.31。
测试的数据库连接信息如下mysql://root:5tzz94jd@unis-sip-mysql.ns-dv9ov434.svc:3306/unis_sip?charset=utf8mb4&parseTime=True&loc=Local
## 系统表
### 系统用户表sys_user
主要用于管理系统用户账号信息,包含用户的基本信息、登录信息和状态等。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| user_id | bigint | 20 | 否 | - | 主键用户ID自增 |
| dept_id | bigint | 20 | 是 | null | 部门ID |
| login_name | varchar | 30 | 否 | - | 登录账号 |
| user_name | varchar | 30 | 是 | '' | 用户昵称 |
| email | varchar | 50 | 是 | '' | 用户邮箱 |
| phonenumber | varchar | 11 | 是 | '' | 手机号码 |
| sex | char | 1 | 是 | '0' | 用户性别0男 1女 2未知 |
| avatar | varchar | 100 | 是 | '' | 头像路径 |
| password | varchar | 50 | 是 | '' | 密码 |
| salt | varchar | 20 | 是 | '' | 盐加密 |
| status | char | 1 | 是 | '0' | 帐号状态0正常 1停用 |
| login_ip | varchar | 128 | 是 | '' | 最后登录IP |
| login_date | datetime | - | 是 | null | 最后登录时间 |
| create_at | datetime | - | 否 | - | 创建时间 |
| update_at | datetime | - | 否 | - | 更新时间 |
| delete_at | datetime | - | 是 | null | 删除时间,软删除 |
索引:
- PRIMARY KEY (user_id)
### 字典类型表sys_dict_type
主要用于管理系统中的各种码表类型定义,包括字典名称、类型和状态等。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| dict_id | bigint | 20 | 否 | - | 主键字典ID自增 |
| dict_name | varchar | 100 | 是 | '' | 字典名称 |
| dict_type | varchar | 100 | 是 | '' | 字典类型 |
| status | char | 1 | 是 | '0' | 状态0正常 1停用 |
| create_at | datetime | - | 否 | - | 创建时间 |
| update_at | datetime | - | 否 | - | 更新时间 |
| delete_at | datetime | - | 是 | null | 删除时间,软删除 |
| remark | varchar | 500 | 是 | null | 备注 |
索引:
- PRIMARY KEY (dict_id)
- UNIQUE KEY idx_dict_type (dict_type)
### 字典数据表sys_dict_data
主要用于存储字典类型下的字典项数据,包括字典标签、键值和排序等。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| dict_code | bigint | 20 | 否 | - | 主键,字典编码,自增 |
| dict_sort | int | 4 | 是 | 0 | 字典排序 |
| dict_label | varchar | 100 | 是 | '' | 字典标签 |
| dict_value | varchar | 100 | 是 | '' | 字典键值 |
| dict_type | varchar | 100 | 是 | '' | 字典类型 |
| css_class | varchar | 100 | 是 | null | 样式属性(其他样式扩展) |
| is_default | char | 1 | 是 | 'N' | 是否默认Y是 N否 |
| status | char | 1 | 是 | '0' | 状态0正常 1停用 |
| create_at | datetime | - | 否 | - | 创建时间 |
| update_at | datetime | - | 否 | - | 更新时间 |
| remark | varchar | 500 | 是 | null | 备注 |
索引:
- PRIMARY KEY (dict_code)
- KEY idx_dict_type (dict_type)
### 系统角色表sys_role
主要用于管理系统角色权限,包含角色的基本信息、数据权限范围和状态等。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| role_id | bigint | 20 | 否 | - | 主键角色ID自增 |
| role_name | varchar | 30 | 否 | - | 角色名称 |
| role_key | varchar | 100 | 否 | - | 角色权限字符串 |
| data_scope | char | 1 | 是 | '1' | 数据范围1全部数据权限 2自定数据权限 3本部门数据权限 4本部门及以下数据权限 |
| status | char | 1 | 否 | - | 角色状态0正常 1停用 |
| create_time | datetime | - | 是 | null | 创建时间 |
| update_time | datetime | - | 是 | null | 更新时间 |
| delete_at | datetime | - | 是 | null | 删除时间 |
索引:
- PRIMARY KEY (role_id)
### 用户角色关系表sys_user_role
主要用于管理用户和角色的多对多关联关系。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| id | bigint | 20 | 否 | - | 主键ID自增 |
| user_id | bigint | 20 | 否 | - | 用户ID |
| role_id | bigint | 20 | 否 | - | 角色ID |
索引:
- PRIMARY KEY (id)
### 部门信息表sys_dept
主要用于管理组织架构,包含部门的基本信息、层级关系和联系方式等。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| dept_id | bigint | 20 | 否 | - | 主键部门id自增 |
| parent_id | bigint | 20 | 是 | 0 | 父部门id |
| ancestors | varchar | 50 | 是 | '' | 祖级列表 |
| dept_name | varchar | 30 | 是 | '' | 部门名称 |
| order_num | int | 4 | 是 | 0 | 显示顺序 |
| contact_person | varchar | 20 | 是 | null | 联系人 |
| contact_phone | varchar | 11 | 是 | null | 联系电话 |
| contact_email | varchar | 50 | 是 | null | 联系邮箱 |
| status | char | 1 | 是 | '0' | 部门状态0-正常1-停用 |
| create_at | datetime | - | 否 | - | 创建时间 |
| update_at | datetime | - | 否 | - | 更新时间 |
| delete_at | datetime | - | 是 | null | 删除时间,软删除 |
索引:
- PRIMARY KEY (dept_id)
### 菜单权限表sys_menu
主要用于管理系统菜单权限,包含菜单的基本信息、层级关系和权限标识等。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| menu_id | bigint | 20 | 否 | - | 主键菜单ID自增 |
| menu_name | varchar | 50 | 否 | - | 菜单名称 |
| parent_id | bigint | 20 | 是 | 0 | 父菜单ID |
| order_num | int | 4 | 是 | 0 | 显示顺序 |
| url | varchar | 200 | 是 | '#' | 路由地址 |
| target | varchar | 20 | 是 | '' | 打开方式menuItem页签 menuBlank新窗口 |
| menu_type | char | 1 | 是 | '' | 菜单类型M模块 C菜单 F按钮 |
| perms | varchar | 100 | 是 | null | 权限标识 |
| icon | varchar | 100 | 是 | '#' | 菜单图标 |
| create_at | datetime | - | 否 | - | 创建时间 |
| update_at | datetime | - | 否 | - | 更新时间 |
| delete_at | datetime | - | 是 | null | 删除时间 |
| remark | varchar | 500 | 是 | '' | 备注 |
索引:
- PRIMARY KEY (menu_id)
### 角色菜单关系表sys_role_menu
主要用于管理角色和菜单的多对多关联关系。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| id | bigint | 20 | 否 | - | 主键ID自增 |
| role_id | bigint | 20 | 否 | - | 角色ID |
| menu_id | bigint | 20 | 否 | - | 菜单ID |
索引:
- PRIMARY KEY (id)
## 业务表
### 产品编码表product_code
主要用于存储产品的基本信息,包括产品型号、规格等。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| id | bigint | - | 否 | - | 主键,自增 |
| product_code | varchar | 64 | 否 | - | 产品编码,唯一 |
| product_name | varchar | 128 | 否 | - | 产品名称 |
| model | varchar | 64 | 否 | - | 产品代码 |
| description | text | - | 是 | null | 产品描述 |
| unit | varchar | 16 | 否 | '个' | 单位 |
| remark | varchar | 512 | 是 | null | 备注 |
| created_at | datetime | - | 否 | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | datetime | - | 否 | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | datetime | - | 是 | null | 删除时间,软删除 |
索引:
- PRIMARY KEY (id)
- UNIQUE KEY idx_product_code (product_code)
- KEY idx_model (model)
### 项目信息表project_info
主要用于存储项目的基本信息,包括项目状态、客户信息等。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| id | bigint | - | 否 | - | 主键,自增 |
| project_code | varchar | 32 | 否 | - | 项目编号,唯一 |
| project_name | varchar | 128 | 否 | - | 项目名称 |
| customer_name | varchar | 64 | 否 | - | 客户名称 |
| customer_contact | varchar | 32 | 是 | null | 客户联系人 |
| customer_phone | varchar | 20 | 是 | null | 客户联系电话 |
| customer_email | varchar | 64 | 是 | null | 客户邮箱 |
| project_status | tinyint | - | 否 | 1 | 项目状态1-进行中2-已签合同3-已交付99-已终止 |
| bg_property | tinyint | - | 否 | - | BG属性1-商业2-行业 |
| industry_code | tinyint | - | 否 | - | 行业编码 |
| project_manager | varchar | 32 | 否 | - | 项目经理 |
| project_manager_phone | varchar | 20 | 是 | null | 项目经理电话 |
| project_manager_email | varchar | 64 | 是 | null | 项目经理邮箱 |
| start_date | date | - | 否 | - | 项目开始日期 |
| description | text | - | 是 | null | 项目描述 |
| created_at | datetime | - | 否 | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | datetime | - | 否 | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | datetime | - | 是 | null | 删除时间,软删除 |
索引:
- PRIMARY KEY (id)
- UNIQUE KEY idx_project_code (project_code)
- KEY idx_customer_name (customer_name)
- KEY idx_project_status (project_status)
### 合同信息表order_info
主要用于存储项目的合同信息,与项目信息表为一对多关系。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| id | bigint | - | 否 | - | 主键,自增 |
| project_code | varchar | 32 | 否 | - | 关联项目编号 |
| order_code | varchar | 32 | 否 | - | 合同编号,唯一 |
| order_name | varchar | 128 | 否 | - | 合同名称 |
| customer_name | varchar | 64 | 否 | - | 客户名称 |
| customer_contact | varchar | 32 | 是 | null | 客户联系人 |
| customer_phone | varchar | 20 | 是 | null | 客户联系电话 |
| customer_email | varchar | 64 | 是 | null | 客户邮箱 |
| order_type | tinyint | - | 否 | - | 合同类型1-直签合同2-代理商合同 |
| order_dept | varchar | 32 | 否 | - | 归属代表处编码 |
| partener_dept | varchar | 32 | 否 | - | 代理商编码 |
| order_date | date | - | 否 | - | 合同签定日期 |
| order_status | tinyint | - | 否 | 1 | 合同状态1-待审核2-已审核3-已驳回 |
| remark | varchar | 512 | 是 | null | 备注 |
| created_at | datetime | - | 否 | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | datetime | - | 否 | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | datetime | - | 是 | null | 删除时间,软删除 |
索引:
- PRIMARY KEY (id)
- UNIQUE KEY idx_order_code (order_code)
### 合同清单表order_list
主要用于记录合同包含的产品信息,包括产品编码、单价和数量等。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| id | bigint | - | 否 | - | 主键,自增 |
| order_code | varchar | 32 | 否 | - | 关联合同编号 |
| product_code | varchar | 64 | 否 | - | 产品编码,关联产品编码表 |
| quantity | int | - | 否 | 1 | 数量 |
| price | decimal | 10,2 | 否 | 0 | 单价 |
| amount | decimal | 10,2 | 否 | 0 | 总价 |
| remark | varchar | 512 | 是 | null | 备注 |
| created_at | datetime | - | 否 | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | datetime | - | 否 | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | datetime | - | 是 | null | 删除时间,软删除 |
索引:
- PRIMARY KEY (id)
- KEY idx_order_id (order_id)
- KEY idx_product_code (product_code)
### 发货记录表order_delivery
主要用于记录产品的发货信息,包括发货时间、物流信息等。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| id | bigint | - | 否 | - | 主键,自增 |
| order_code | varchar | 32 | 否 | - | 关联合同编号 |
| delivery_code | varchar | 32 | 否 | - | 发货单号,唯一 |
| delivery_date | date | - | 否 | - | 发货日期 |
| delivery_type | tinyint | - | 否 | 1 | 发货方式1-快递2-物流3-自提 |
| logistics_company | varchar | 64 | 是 | null | 物流公司 |
| logistics_code | varchar | 32 | 是 | null | 物流单号 |
| receiver_name | varchar | 32 | 否 | - | 收货人姓名 |
| receiver_phone | varchar | 20 | 否 | - | 收货人电话 |
| receiver_address | varchar | 256 | 否 | - | 收货地址 |
| delivery_status | tinyint | - | 否 | 1 | 发货状态1-待发货2-已发货3-已签收 |
| sign_time | datetime | - | 是 | null | 签收时间 |
| remark | varchar | 512 | 是 | null | 备注 |
| created_at | datetime | - | 否 | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | datetime | - | 否 | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | datetime | - | 是 | null | 删除时间,软删除 |
索引:
- PRIMARY KEY (id)
- UNIQUE KEY idx_delivery_code (delivery_code)
- KEY idx_order_id (order_id)
- KEY idx_delivery_status (delivery_status)
### 发货清单表delivery_list
主要用于记录每个发货单包含的具体产品信息,包括产品编码和序列号等。
| 字段名 | 类型 | 长度 | 允许空 | 默认值 | 说明 |
|--------|------|------|--------|--------|------|
| id | bigint | - | 否 | - | 主键,自增 |
| delivery_code | varchar | 32 | 否 | - | 关联发货单编号 |
| product_code | varchar | 64 | 否 | - | 产品编码,关联产品编码表 |
| serial_number | varchar | 64 | 否 | - | 产品序列号 |
| remark | varchar | 512 | 是 | null | 备注 |
| created_at | datetime | - | 否 | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | datetime | - | 否 | CURRENT_TIMESTAMP | 更新时间 |
| deleted_at | datetime | - | 是 | null | 删除时间,软删除 |
索引:
- PRIMARY KEY (id)
- KEY idx_delivery_id (delivery_id)
- KEY idx_product_code (product_code)
- UNIQUE KEY idx_serial_number (serial_number)

View File

@ -0,0 +1,14 @@
package com.unissense.sip;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.unissense.sip.mapper")
public class UnisSipApplication {
public static void main(String[] args) {
SpringApplication.run(UnisSipApplication.class, args);
}
}

View File

@ -0,0 +1,13 @@
package com.unissense.sip.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
public class SecurityConfig {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@ -0,0 +1,48 @@
package com.unissense.sip.controller;
import com.unissense.sip.dto.LoginRequest;
import com.unissense.sip.dto.LoginResponse;
import com.unissense.sip.service.SysUserService;
import com.unissense.sip.utils.CaptchaUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/api")
public class AuthController {
@Autowired
private SysUserService userService;
@Autowired
private StringRedisTemplate redisTemplate;
@GetMapping("/captcha")
public void getCaptcha(@RequestParam String username, HttpServletResponse response) throws Exception {
StringBuilder captcha = new StringBuilder();
BufferedImage image = CaptchaUtils.generateCaptchaImage(captcha);
// 将验证码存入Redis设置60秒过期
String captchaKey = "captcha:" + username;
redisTemplate.opsForValue().set(captchaKey, captcha.toString(), 60, TimeUnit.SECONDS);
// 输出图片
response.setContentType(MediaType.IMAGE_PNG_VALUE);
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image, "png", os);
response.getOutputStream().write(os.toByteArray());
}
@PostMapping("/login")
public LoginResponse login(@RequestBody LoginRequest loginRequest) {
return userService.login(loginRequest);
}
}

View File

@ -0,0 +1,32 @@
package com.unissense.sip.controller;
import com.unissense.sip.entity.SysDept;
import com.unissense.sip.service.SysDeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/depts")
public class SysDeptController {
@Autowired
private SysDeptService sysDeptService;
@GetMapping("/{deptCode}")
public ResponseEntity<SysDept> getDeptByCode(@PathVariable String deptCode) {
SysDept dept = sysDeptService.getByDeptCode(deptCode);
return dept != null ? ResponseEntity.ok(dept) : ResponseEntity.notFound().build();
}
@PostMapping
public ResponseEntity<Boolean> createDept(@RequestBody SysDept dept) {
boolean success = sysDeptService.createDept(dept);
return success ? ResponseEntity.ok(true) : ResponseEntity.badRequest().build();
}
@GetMapping
public ResponseEntity<?> listDepts() {
return ResponseEntity.ok(sysDeptService.list());
}
}

View File

@ -0,0 +1,80 @@
package com.unissense.sip.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.unissense.sip.entity.SysDictData;
import com.unissense.sip.service.SysDictDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
/**
* Controller
*/
@RestController
@RequestMapping("/system/dict/data")
public class SysDictDataController {
@Autowired
private SysDictDataService dictDataService;
/**
*
*/
@GetMapping("/list")
public Page<SysDictData> list(
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
SysDictData dictData) {
LambdaQueryWrapper<SysDictData> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(dictData.getDictType() != null, SysDictData::getDictType, dictData.getDictType())
.like(dictData.getDictLabel() != null, SysDictData::getDictLabel, dictData.getDictLabel())
.eq(dictData.getStatus() != null, SysDictData::getStatus, dictData.getStatus())
.orderByAsc(SysDictData::getDictSort);
return dictDataService.page(new Page<>(pageNum, pageSize), wrapper);
}
/**
*
*/
@GetMapping(value = "/type/{dictType}")
public List<SysDictData> dictType(@PathVariable String dictType) {
return dictDataService.selectDictDataByType(dictType);
}
/**
*
*/
@GetMapping("/{dictCode}")
public SysDictData getInfo(@PathVariable Long dictCode) {
return dictDataService.getById(dictCode);
}
/**
*
*/
@PostMapping
public boolean add(@RequestBody SysDictData dict) {
dict.setCreateTime(LocalDateTime.now());
return dictDataService.save(dict);
}
/**
*
*/
@PutMapping
public boolean edit(@RequestBody SysDictData dict) {
dict.setUpdateTime(LocalDateTime.now());
return dictDataService.updateById(dict);
}
/**
*
*/
@DeleteMapping("/{dictCode}")
public boolean remove(@PathVariable Long dictCode) {
return dictDataService.removeById(dictCode);
}
}

View File

@ -0,0 +1,71 @@
package com.unissense.sip.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.unissense.sip.entity.SysDictType;
import com.unissense.sip.service.SysDictTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
/**
* Controller
*/
@RestController
@RequestMapping("/system/dict/type")
public class SysDictTypeController {
@Autowired
private SysDictTypeService dictTypeService;
/**
*
*/
@GetMapping("/list")
public Page<SysDictType> list(
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
SysDictType dictType) {
LambdaQueryWrapper<SysDictType> wrapper = new LambdaQueryWrapper<>();
wrapper.like(dictType.getDictName() != null, SysDictType::getDictName, dictType.getDictName())
.like(dictType.getDictType() != null, SysDictType::getDictType, dictType.getDictType())
.eq(dictType.getStatus() != null, SysDictType::getStatus, dictType.getStatus())
.orderByAsc(SysDictType::getDictId);
return dictTypeService.page(new Page<>(pageNum, pageSize), wrapper);
}
/**
*
*/
@GetMapping("/{dictId}")
public SysDictType getInfo(@PathVariable Long dictId) {
return dictTypeService.getById(dictId);
}
/**
*
*/
@PostMapping
public boolean add(@RequestBody SysDictType dict) {
dict.setCreateTime(LocalDateTime.now());
return dictTypeService.save(dict);
}
/**
*
*/
@PutMapping
public boolean edit(@RequestBody SysDictType dict) {
dict.setUpdateTime(LocalDateTime.now());
return dictTypeService.updateById(dict);
}
/**
*
*/
@DeleteMapping("/{dictId}")
public boolean remove(@PathVariable Long dictId) {
return dictTypeService.removeById(dictId);
}
}

View File

@ -0,0 +1,40 @@
package com.unissense.sip.controller;
import com.unissense.sip.entity.SysMenu;
import com.unissense.sip.service.SysMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/menus")
public class SysMenuController {
@Autowired
private SysMenuService sysMenuService;
@GetMapping("/{menuCode}")
public ResponseEntity<SysMenu> getMenuByCode(@PathVariable String menuCode) {
SysMenu menu = sysMenuService.getByMenuCode(menuCode);
return menu != null ? ResponseEntity.ok(menu) : ResponseEntity.notFound().build();
}
@PostMapping
public ResponseEntity<Boolean> createMenu(@RequestBody SysMenu menu) {
boolean success = sysMenuService.createMenu(menu);
return success ? ResponseEntity.ok(true) : ResponseEntity.badRequest().build();
}
@GetMapping
public ResponseEntity<?> listMenus() {
return ResponseEntity.ok(sysMenuService.list());
}
@GetMapping("/children/{parentId}")
public ResponseEntity<List<SysMenu>> getChildrenMenus(@PathVariable Long parentId) {
List<SysMenu> children = sysMenuService.getChildrenMenus(parentId);
return ResponseEntity.ok(children);
}
}

View File

@ -0,0 +1,26 @@
package com.unissense.sip.controller;
import com.unissense.sip.entity.SysRole;
import com.unissense.sip.service.SysRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/roles")
public class SysRoleController {
@Autowired
private SysRoleService sysRoleService;
@PostMapping
public ResponseEntity<Boolean> createRole(@RequestBody SysRole role) {
boolean success = sysRoleService.createRole(role);
return success ? ResponseEntity.ok(true) : ResponseEntity.badRequest().build();
}
@GetMapping
public ResponseEntity<?> listRoles() {
return ResponseEntity.ok(sysRoleService.list());
}
}

View File

@ -0,0 +1,32 @@
package com.unissense.sip.controller;
import com.unissense.sip.entity.SysUser;
import com.unissense.sip.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class SysUserController {
@Autowired
private SysUserService sysUserService;
@GetMapping("/{username}")
public ResponseEntity<SysUser> getUserByUsername(@PathVariable String username) {
SysUser user = sysUserService.getByUsername(username);
return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
}
@PostMapping
public ResponseEntity<Boolean> createUser(@RequestBody SysUser user) {
boolean result = sysUserService.createUser(user);
return ResponseEntity.ok(result);
}
@GetMapping("/test")
public ResponseEntity<String> test() {
return ResponseEntity.ok("User API is working!");
}
}

View File

@ -0,0 +1,36 @@
package com.unissense.sip.controller;
import com.unissense.sip.service.SysUserRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/user-roles")
public class SysUserRoleController {
@Autowired
private SysUserRoleService sysUserRoleService;
@GetMapping("/user/{userId}/roles")
public ResponseEntity<List<Long>> getUserRoles(@PathVariable Long userId) {
List<Long> roleIds = sysUserRoleService.getRoleIdsByUserId(userId);
return ResponseEntity.ok(roleIds);
}
@PostMapping("/user/{userId}/roles")
public ResponseEntity<Boolean> assignRoles(
@PathVariable Long userId,
@RequestBody List<Long> roleIds) {
boolean success = sysUserRoleService.assignRoles(userId, roleIds);
return success ? ResponseEntity.ok(true) : ResponseEntity.badRequest().build();
}
@DeleteMapping("/user/{userId}/roles")
public ResponseEntity<Boolean> removeUserRoles(@PathVariable Long userId) {
boolean success = sysUserRoleService.removeUserRoles(userId);
return success ? ResponseEntity.ok(true) : ResponseEntity.badRequest().build();
}
}

View File

@ -0,0 +1,10 @@
package com.unissense.sip.dto;
import lombok.Data;
@Data
public class LoginRequest {
private String username;
private String password;
private String captcha;
}

View File

@ -0,0 +1,15 @@
package com.unissense.sip.dto;
import com.unissense.sip.entity.SysMenu;
import com.unissense.sip.entity.SysUser;
import lombok.Data;
import java.util.List;
@Data
public class LoginResponse {
private String token;
private SysUser user;
private List<String> roles;
private List<SysMenu> menus;
}

View File

@ -0,0 +1,46 @@
package com.unissense.sip.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_dept")
public class SysDept {
@TableId(type = IdType.AUTO)
private Long deptId;
private Long parentId;
private String deptName;
private Integer orderNum;
private String leader;
private String phone;
private String email;
private Integer status;
private String deptCode;
private LocalDateTime deleteAt;
private LocalDateTime createAt;
private String createBy;
private LocalDateTime createTime;
private String updateBy;
private LocalDateTime updateTime;
private String remark;
}

View File

@ -0,0 +1,40 @@
package com.unissense.sip.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_dict_data")
public class SysDictData {
@TableId(type = IdType.AUTO)
private Long dictCode;
private Long dictSort;
private String dictLabel;
private String dictValue;
private String dictType;
private String cssClass;
private String listClass;
private Integer status;
private String createBy;
private LocalDateTime createTime;
private String updateBy;
private LocalDateTime updateTime;
private String remark;
}

View File

@ -0,0 +1,32 @@
package com.unissense.sip.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_dict_type")
public class SysDictType {
@TableId(type = IdType.AUTO)
private Long dictId;
private String dictName;
private String dictType;
private Integer status;
private String createBy;
private LocalDateTime createTime;
private String updateBy;
private LocalDateTime updateTime;
private String remark;
}

View File

@ -0,0 +1,50 @@
package com.unissense.sip.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_menu")
public class SysMenu {
@TableId(type = IdType.AUTO)
private Long menuId;
private Long parentId;
private String menuName;
private String path;
private String component;
private String perms;
private String icon;
private Integer type;
private Integer orderNum;
private Integer status;
private String menuCode;
private LocalDateTime deleteAt;
private LocalDateTime createAt;
private String createBy;
private LocalDateTime createTime;
private String updateBy;
private LocalDateTime updateTime;
private String remark;
}

View File

@ -0,0 +1,40 @@
package com.unissense.sip.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_role")
public class SysRole {
@TableId(type = IdType.AUTO)
private Long roleId;
private String roleName;
private String roleKey;
private Integer roleSort;
private String dataScope;
private Integer status;
private LocalDateTime deleteAt;
private LocalDateTime createAt;
private String createBy;
private LocalDateTime createTime;
private String updateBy;
private LocalDateTime updateTime;
private String remark;
}

View File

@ -0,0 +1,18 @@
package com.unissense.sip.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_role_menu")
public class SysRoleMenu {
@TableId(type = IdType.AUTO)
private Long id;
private Long roleId;
private Long menuId;
}

View File

@ -0,0 +1,31 @@
package com.unissense.sip.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_user")
public class SysUser {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private String email;
private String phone;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private LocalDateTime deleteAt;
}

View File

@ -0,0 +1,18 @@
package com.unissense.sip.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_user_role")
public class SysUserRole {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private Long roleId;
}

View File

@ -0,0 +1,9 @@
package com.unissense.sip.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.unissense.sip.entity.SysDept;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysDeptMapper extends BaseMapper<SysDept> {
}

View File

@ -0,0 +1,9 @@
package com.unissense.sip.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.unissense.sip.entity.SysDictData;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysDictDataMapper extends BaseMapper<SysDictData> {
}

View File

@ -0,0 +1,9 @@
package com.unissense.sip.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.unissense.sip.entity.SysDictType;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysDictTypeMapper extends BaseMapper<SysDictType> {
}

View File

@ -0,0 +1,9 @@
package com.unissense.sip.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.unissense.sip.entity.SysMenu;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysMenuMapper extends BaseMapper<SysMenu> {
}

View File

@ -0,0 +1,9 @@
package com.unissense.sip.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.unissense.sip.entity.SysRole;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysRoleMapper extends BaseMapper<SysRole> {
}

View File

@ -0,0 +1,9 @@
package com.unissense.sip.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.unissense.sip.entity.SysRoleMenu;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenu> {
}

View File

@ -0,0 +1,9 @@
package com.unissense.sip.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.unissense.sip.entity.SysUser;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
}

View File

@ -0,0 +1,9 @@
package com.unissense.sip.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.unissense.sip.entity.SysUserRole;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysUserRoleMapper extends BaseMapper<SysUserRole> {
}

View File

@ -0,0 +1,20 @@
package com.unissense.sip.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.unissense.sip.entity.SysDept;
public interface SysDeptService extends IService<SysDept> {
/**
*
* @param deptCode
* @return
*/
SysDept getByDeptCode(String deptCode);
/**
*
* @param dept
* @return
*/
boolean createDept(SysDept dept);
}

View File

@ -0,0 +1,29 @@
package com.unissense.sip.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.unissense.sip.entity.SysDictData;
import java.util.List;
/**
* Service
*/
public interface SysDictDataService extends IService<SysDictData> {
/**
*
*
* @param dictType
* @return
*/
List<SysDictData> selectDictDataByType(String dictType);
/**
*
*
* @param dictType
* @param dictValue
* @return
*/
String selectDictLabel(String dictType, String dictValue);
}

View File

@ -0,0 +1,18 @@
package com.unissense.sip.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.unissense.sip.entity.SysDictType;
/**
* Service
*/
public interface SysDictTypeService extends IService<SysDictType> {
/**
*
*
* @param dictType
* @return
*/
SysDictType selectDictTypeByType(String dictType);
}

View File

@ -0,0 +1,29 @@
package com.unissense.sip.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.unissense.sip.entity.SysMenu;
import java.util.List;
public interface SysMenuService extends IService<SysMenu> {
/**
*
* @param menuCode
* @return
*/
SysMenu getByMenuCode(String menuCode);
/**
*
* @param menu
* @return
*/
boolean createMenu(SysMenu menu);
/**
*
* @param parentId ID
* @return
*/
List<SysMenu> getChildrenMenus(Long parentId);
}

View File

@ -0,0 +1,30 @@
package com.unissense.sip.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.unissense.sip.entity.SysRoleMenu;
import java.util.List;
public interface SysRoleMenuService extends IService<SysRoleMenu> {
/**
* ID
* @param roleId ID
* @return ID
*/
List<Long> getMenuIdsByRoleId(Long roleId);
/**
*
* @param roleId ID
* @param menuIds ID
* @return
*/
boolean assignMenus(Long roleId, List<Long> menuIds);
/**
*
* @param roleId ID
* @return
*/
boolean removeRoleMenus(Long roleId);
}

View File

@ -0,0 +1,14 @@
package com.unissense.sip.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.unissense.sip.entity.SysRole;
public interface SysRoleService extends IService<SysRole> {
/**
*
* @param role
* @return
*/
boolean createRole(SysRole role);
}

View File

@ -0,0 +1,45 @@
package com.unissense.sip.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.unissense.sip.entity.SysUserRole;
import com.unissense.sip.entity.SysMenu;
import java.util.List;
public interface SysUserRoleService extends IService<SysUserRole> {
/**
* ID
* @param userId ID
* @return ID
*/
List<Long> getRoleIdsByUserId(Long userId);
/**
*
* @param userId ID
* @param roleIds ID
* @return
*/
boolean assignRoles(Long userId, List<Long> roleIds);
/**
*
* @param userId ID
* @return
*/
boolean removeUserRoles(Long userId);
/**
*
* @param userId ID
* @return
*/
List<String> getRoleKeysByUserId(Long userId);
/**
*
* @param userId ID
* @return
*/
List<SysMenu> getMenusByUserId(Long userId);
}

View File

@ -0,0 +1,46 @@
package com.unissense.sip.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.unissense.sip.dto.LoginRequest;
import com.unissense.sip.dto.LoginResponse;
import com.unissense.sip.entity.SysMenu;
import com.unissense.sip.entity.SysUser;
import java.util.List;
public interface SysUserService extends IService<SysUser> {
/**
*
* @param username
* @return
*/
SysUser getByUsername(String username);
/**
*
* @param user
* @return
*/
boolean createUser(SysUser user);
/**
*
* @param loginRequest
* @return
*/
LoginResponse login(LoginRequest loginRequest);
/**
*
* @param userId ID
* @return
*/
List<String> getUserRoles(Long userId);
/**
*
* @param userId ID
* @return
*/
List<SysMenu> getUserMenus(Long userId);
}

View File

@ -0,0 +1,27 @@
package com.unissense.sip.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unissense.sip.entity.SysDept;
import com.unissense.sip.mapper.SysDeptMapper;
import com.unissense.sip.service.SysDeptService;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements SysDeptService {
@Override
public SysDept getByDeptCode(String deptCode) {
return this.getOne(new LambdaQueryWrapper<SysDept>()
.eq(SysDept::getDeptCode, deptCode)
.isNull(SysDept::getDeleteAt));
}
@Override
public boolean createDept(SysDept dept) {
dept.setCreateAt(LocalDateTime.now());
return this.save(dept);
}
}

View File

@ -0,0 +1,34 @@
package com.unissense.sip.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unissense.sip.entity.SysDictData;
import com.unissense.sip.mapper.SysDictDataMapper;
import com.unissense.sip.service.SysDictDataService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* Service
*/
@Service
public class SysDictDataServiceImpl extends ServiceImpl<SysDictDataMapper, SysDictData> implements SysDictDataService {
@Override
public List<SysDictData> selectDictDataByType(String dictType) {
LambdaQueryWrapper<SysDictData> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysDictData::getDictType, dictType)
.orderByAsc(SysDictData::getDictSort);
return list(wrapper);
}
@Override
public String selectDictLabel(String dictType, String dictValue) {
LambdaQueryWrapper<SysDictData> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysDictData::getDictType, dictType)
.eq(SysDictData::getDictValue, dictValue);
SysDictData dictData = getOne(wrapper);
return dictData != null ? dictData.getDictLabel() : "";
}
}

View File

@ -0,0 +1,22 @@
package com.unissense.sip.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unissense.sip.entity.SysDictType;
import com.unissense.sip.mapper.SysDictTypeMapper;
import com.unissense.sip.service.SysDictTypeService;
import org.springframework.stereotype.Service;
/**
* Service
*/
@Service
public class SysDictTypeServiceImpl extends ServiceImpl<SysDictTypeMapper, SysDictType> implements SysDictTypeService {
@Override
public SysDictType selectDictTypeByType(String dictType) {
LambdaQueryWrapper<SysDictType> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysDictType::getDictType, dictType);
return getOne(wrapper);
}
}

View File

@ -0,0 +1,36 @@
package com.unissense.sip.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unissense.sip.entity.SysMenu;
import com.unissense.sip.mapper.SysMenuMapper;
import com.unissense.sip.service.SysMenuService;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
@Override
public SysMenu getByMenuCode(String menuCode) {
return this.getOne(new LambdaQueryWrapper<SysMenu>()
.eq(SysMenu::getMenuCode, menuCode)
.isNull(SysMenu::getDeleteAt));
}
@Override
public boolean createMenu(SysMenu menu) {
menu.setCreateAt(LocalDateTime.now());
return this.save(menu);
}
@Override
public List<SysMenu> getChildrenMenus(Long parentId) {
return this.list(new LambdaQueryWrapper<SysMenu>()
.eq(SysMenu::getParentId, parentId)
.isNull(SysMenu::getDeleteAt)
.orderByAsc(SysMenu::getOrderNum));
}
}

View File

@ -0,0 +1,51 @@
package com.unissense.sip.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unissense.sip.entity.SysRoleMenu;
import com.unissense.sip.mapper.SysRoleMenuMapper;
import com.unissense.sip.service.SysRoleMenuService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuMapper, SysRoleMenu> implements SysRoleMenuService {
@Override
public List<Long> getMenuIdsByRoleId(Long roleId) {
return this.list(new LambdaQueryWrapper<SysRoleMenu>()
.eq(SysRoleMenu::getRoleId, roleId))
.stream()
.map(SysRoleMenu::getMenuId)
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean assignMenus(Long roleId, List<Long> menuIds) {
// 先删除原有的角色菜单关系
this.removeRoleMenus(roleId);
// 构建新的角色菜单关系
List<SysRoleMenu> roleMenus = new ArrayList<>();
for (Long menuId : menuIds) {
SysRoleMenu roleMenu = new SysRoleMenu();
roleMenu.setRoleId(roleId);
roleMenu.setMenuId(menuId);
roleMenus.add(roleMenu);
}
// 批量保存新的角色菜单关系
return this.saveBatch(roleMenus);
}
@Override
public boolean removeRoleMenus(Long roleId) {
return this.remove(new LambdaQueryWrapper<SysRoleMenu>()
.eq(SysRoleMenu::getRoleId, roleId));
}
}

View File

@ -0,0 +1,20 @@
package com.unissense.sip.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unissense.sip.entity.SysRole;
import com.unissense.sip.mapper.SysRoleMapper;
import com.unissense.sip.service.SysRoleService;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {
@Override
public boolean createRole(SysRole role) {
role.setCreateAt(LocalDateTime.now());
return this.save(role);
}
}

View File

@ -0,0 +1,97 @@
package com.unissense.sip.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unissense.sip.entity.SysUserRole;
import com.unissense.sip.entity.SysRole;
import com.unissense.sip.entity.SysMenu;
import com.unissense.sip.mapper.SysUserRoleMapper;
import com.unissense.sip.service.SysUserRoleService;
import com.unissense.sip.service.SysRoleService;
import com.unissense.sip.service.SysMenuService;
import com.unissense.sip.service.SysRoleMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Collections;
import java.util.stream.Collectors;
@Service
public class SysUserRoleServiceImpl extends ServiceImpl<SysUserRoleMapper, SysUserRole> implements SysUserRoleService {
@Autowired
private SysRoleService roleService;
@Autowired
private SysMenuService menuService;
@Autowired
private SysRoleMenuService roleMenuService;
@Override
public List<Long> getRoleIdsByUserId(Long userId) {
List<SysUserRole> userRoles = this.list(new LambdaQueryWrapper<SysUserRole>()
.eq(SysUserRole::getUserId, userId));
return userRoles.stream()
.map(SysUserRole::getRoleId)
.collect(Collectors.toList());
}
@Override
@Transactional
public boolean assignRoles(Long userId, List<Long> roleIds) {
// 先删除用户现有的角色
this.removeUserRoles(userId);
// 分配新的角色
List<SysUserRole> userRoles = roleIds.stream().map(roleId -> {
SysUserRole userRole = new SysUserRole();
userRole.setUserId(userId);
userRole.setRoleId(roleId);
return userRole;
}).collect(Collectors.toList());
return this.saveBatch(userRoles);
}
@Override
public boolean removeUserRoles(Long userId) {
return this.remove(new LambdaQueryWrapper<SysUserRole>()
.eq(SysUserRole::getUserId, userId));
}
@Override
public List<String> getRoleKeysByUserId(Long userId) {
List<Long> roleIds = this.getRoleIdsByUserId(userId);
if (roleIds.isEmpty()) {
return Collections.emptyList();
}
return roleService.listByIds(roleIds).stream()
.map(SysRole::getRoleKey)
.collect(Collectors.toList());
}
@Override
public List<SysMenu> getMenusByUserId(Long userId) {
List<Long> roleIds = this.getRoleIdsByUserId(userId);
if (roleIds.isEmpty()) {
return Collections.emptyList();
}
// 获取所有角色对应的菜单ID
List<Long> menuIds = roleIds.stream()
.flatMap(roleId -> roleMenuService.getMenuIdsByRoleId(roleId).stream())
.distinct()
.collect(Collectors.toList());
if (menuIds.isEmpty()) {
return Collections.emptyList();
}
// 查询菜单列表
return menuService.listByIds(menuIds);
}
}

View File

@ -0,0 +1,94 @@
package com.unissense.sip.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unissense.sip.dto.LoginRequest;
import com.unissense.sip.dto.LoginResponse;
import com.unissense.sip.entity.SysMenu;
import com.unissense.sip.entity.SysUser;
import com.unissense.sip.mapper.SysUserMapper;
import com.unissense.sip.service.SysUserRoleService;
import com.unissense.sip.service.SysUserService;
import com.unissense.sip.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
@Override
public List<SysMenu> getUserMenus(Long userId) {
return userRoleService.getMenusByUserId(userId);
}
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Autowired
private JwtUtils jwtUtils;
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private SysUserRoleService userRoleService;
@Override
public SysUser getByUsername(String username) {
return this.getOne(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getUsername, username)
.isNull(SysUser::getDeleteAt));
}
@Override
public boolean createUser(SysUser user) {
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
user.setStatus(1); // 默认启用状态
user.setPassword(passwordEncoder.encode(user.getPassword()));
return this.save(user);
}
@Override
public LoginResponse login(LoginRequest loginRequest) {
// 验证验证码
String captchaKey = "captcha:" + loginRequest.getUsername();
String captcha = redisTemplate.opsForValue().get(captchaKey);
if (captcha == null || !captcha.equalsIgnoreCase(loginRequest.getCaptcha())) {
throw new RuntimeException("验证码错误或已过期");
}
redisTemplate.delete(captchaKey);
// 验证用户名和密码
SysUser user = getByUsername(loginRequest.getUsername());
if (user == null || !passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {
throw new RuntimeException("用户名或密码错误");
}
// 生成token
String token = jwtUtils.generateToken(user.getUsername());
// 获取用户角色和菜单权限
List<String> roles = getUserRoles(user.getId());
List<SysMenu> menus = userRoleService.getMenusByUserId(user.getId());
// 构建响应
LoginResponse response = new LoginResponse();
response.setToken(token);
response.setUser(user);
response.setRoles(roles);
response.setMenus(menus);
return response;
}
@Override
public List<String> getUserRoles(Long userId) {
return userRoleService.getRoleKeysByUserId(userId);
}
}

View File

@ -0,0 +1,43 @@
package com.unissense.sip.utils;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
public class CaptchaUtils {
private static final String CHARACTERS = "2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
private static final int WIDTH = 90;
private static final int HEIGHT = 40;
private static final int LENGTH = 4;
private static final int LINES = 5;
public static BufferedImage generateCaptchaImage(StringBuilder captcha) {
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
// 设置背景色
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
// 生成随机字符
Random random = new Random();
g2d.setFont(new Font("Arial", Font.BOLD, 28));
for (int i = 0; i < LENGTH; i++) {
String ch = String.valueOf(CHARACTERS.charAt(random.nextInt(CHARACTERS.length())));
captcha.append(ch);
g2d.setColor(new Color(random.nextInt(88), random.nextInt(188), random.nextInt(255)));
g2d.drawString(ch, (i * 20) + 10, 30);
}
// 添加干扰线
for (int i = 0; i < LINES; i++) {
g2d.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
g2d.drawLine(random.nextInt(WIDTH), random.nextInt(HEIGHT),
random.nextInt(WIDTH), random.nextInt(HEIGHT));
}
g2d.dispose();
return image;
}
}

View File

@ -0,0 +1,49 @@
package com.unissense.sip.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtUtils {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiretime}")
private long expireTime;
public String generateToken(String username) {
Date now = new Date();
Date expiration = new Date(now.getTime() + expireTime * 1000);
return Jwts.builder()
.setSubject(username)
.setIssuedAt(now)
.setExpiration(expiration)
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}

View File

@ -0,0 +1,26 @@
spring:
datasource:
url: jdbc:mysql://unis-sip-mysql.ns-dv9ov434.svc:3306/unis_sip?useSSL=false&serverTimezone=UTC
username: root
password: 5tzz94jd
driver-class-name: com.mysql.cj.jdbc.Driver
# MyBatis Plus配置
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
type-aliases-package: com.unis.sip.entity
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: deleteAt
logic-delete-value: now()
logic-not-delete-value: null
server:
port: 8080
jwt:
secret: unis-sip-secret-key
expiretime: 86400000 # 24 hours in milliseconds

View File

@ -0,0 +1,26 @@
spring:
datasource:
url: jdbc:mysql://unis-sip-mysql.ns-dv9ov434.svc:3306/unis_sip?useSSL=false&serverTimezone=UTC
username: root
password: 5tzz94jd
driver-class-name: com.mysql.cj.jdbc.Driver
# MyBatis Plus配置
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
type-aliases-package: com.unis.sip.entity
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: deleteAt
logic-delete-value: now()
logic-not-delete-value: null
server:
port: 8080
jwt:
secret: unis-sip-secret-key
expiretime: 86400000 # 24 hours in milliseconds

View File

@ -0,0 +1,45 @@
/home/devbox/project/src/main/java/com/unissense/sip/service/SysDictDataService.java
/home/devbox/project/src/main/java/com/unissense/sip/service/impl/SysRoleServiceImpl.java
/home/devbox/project/src/main/java/com/unissense/sip/service/SysRoleService.java
/home/devbox/project/src/main/java/com/unissense/sip/controller/SysRoleController.java
/home/devbox/project/src/main/java/com/unissense/sip/controller/SysUserController.java
/home/devbox/project/src/main/java/com/unissense/sip/entity/SysRole.java
/home/devbox/project/src/main/java/com/unissense/sip/service/impl/SysDeptServiceImpl.java
/home/devbox/project/src/main/java/com/unissense/sip/UnisSipApplication.java
/home/devbox/project/src/main/java/com/unissense/sip/entity/SysUser.java
/home/devbox/project/src/main/java/com/unissense/sip/controller/SysUserRoleController.java
/home/devbox/project/src/main/java/com/unissense/sip/controller/AuthController.java
/home/devbox/project/src/main/java/com/unissense/sip/mapper/SysDeptMapper.java
/home/devbox/project/src/main/java/com/unissense/sip/dto/LoginRequest.java
/home/devbox/project/src/main/java/com/unissense/sip/controller/SysDictDataController.java
/home/devbox/project/src/main/java/com/unissense/sip/entity/SysUserRole.java
/home/devbox/project/src/main/java/com/unissense/sip/service/SysUserRoleService.java
/home/devbox/project/src/main/java/com/unissense/sip/service/impl/SysRoleMenuServiceImpl.java
/home/devbox/project/src/main/java/com/unissense/sip/utils/CaptchaUtils.java
/home/devbox/project/src/main/java/com/unissense/sip/service/impl/SysMenuServiceImpl.java
/home/devbox/project/src/main/java/com/unissense/sip/entity/SysDictType.java
/home/devbox/project/src/main/java/com/unissense/sip/mapper/SysRoleMenuMapper.java
/home/devbox/project/src/main/java/com/unissense/sip/mapper/SysMenuMapper.java
/home/devbox/project/src/main/java/com/unissense/sip/service/SysUserService.java
/home/devbox/project/src/main/java/com/unissense/sip/entity/SysDept.java
/home/devbox/project/src/main/java/com/unissense/sip/entity/SysDictData.java
/home/devbox/project/src/main/java/com/unissense/sip/controller/SysDeptController.java
/home/devbox/project/src/main/java/com/unissense/sip/mapper/SysDictTypeMapper.java
/home/devbox/project/src/main/java/com/unissense/sip/utils/JwtUtils.java
/home/devbox/project/src/main/java/com/unissense/sip/mapper/SysDictDataMapper.java
/home/devbox/project/src/main/java/com/unissense/sip/mapper/SysUserRoleMapper.java
/home/devbox/project/src/main/java/com/unissense/sip/service/impl/SysUserServiceImpl.java
/home/devbox/project/src/main/java/com/unissense/sip/entity/SysRoleMenu.java
/home/devbox/project/src/main/java/com/unissense/sip/controller/SysMenuController.java
/home/devbox/project/src/main/java/com/unissense/sip/service/impl/SysDictDataServiceImpl.java
/home/devbox/project/src/main/java/com/unissense/sip/mapper/SysRoleMapper.java
/home/devbox/project/src/main/java/com/unissense/sip/entity/SysMenu.java
/home/devbox/project/src/main/java/com/unissense/sip/controller/SysDictTypeController.java
/home/devbox/project/src/main/java/com/unissense/sip/dto/LoginResponse.java
/home/devbox/project/src/main/java/com/unissense/sip/service/SysDictTypeService.java
/home/devbox/project/src/main/java/com/unissense/sip/service/SysDeptService.java
/home/devbox/project/src/main/java/com/unissense/sip/service/SysRoleMenuService.java
/home/devbox/project/src/main/java/com/unissense/sip/service/impl/SysDictTypeServiceImpl.java
/home/devbox/project/src/main/java/com/unissense/sip/service/impl/SysUserRoleServiceImpl.java
/home/devbox/project/src/main/java/com/unissense/sip/service/SysMenuService.java
/home/devbox/project/src/main/java/com/unissense/sip/mapper/SysUserMapper.java

228
template/index.html 100644
View File

@ -0,0 +1,228 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>售后服务平台</title>
<link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css">
<style>
body {
margin: 0;
padding: 0;
height: 100vh;
}
.layout-container {
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
background-color: #001529;
color: #fff;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
height: 60px;
}
.logo {
font-size: 20px;
font-weight: bold;
}
.main-container {
flex: 1;
display: flex;
overflow: hidden;
}
.sidebar {
width: 220px;
height: 100%;
background-color: #001529;
transition: width 0.3s;
}
.sidebar.collapsed {
width: 64px;
}
.content {
flex: 1;
padding: 20px;
overflow-y: auto;
background-color: #f5f7fa;
}
.user-info {
display: flex;
align-items: center;
gap: 10px;
color: #fff;
}
.el-menu {
border-right: none !important;
}
</style>
</head>
<body>
<div id="app">
<div class="layout-container">
<header class="header">
<div class="logo">
<el-button type="text" style="color: #fff" @click="toggleSidebar">
<el-icon><Fold v-if="!isCollapse" /><Expand v-else /></el-icon>
</el-button>
<span v-show="!isCollapse">售后服务平台</span>
</div>
<div class="user-info">
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
{{ userInfo.userName }}<el-icon class="el-icon--right"><arrow-down /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="profile">个人信息</el-dropdown-item>
<el-dropdown-item command="password">修改密码</el-dropdown-item>
<el-dropdown-item divided command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</header>
<div class="main-container">
<div class="sidebar" :class="{ collapsed: isCollapse }">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
background-color="#001529"
text-color="#fff"
active-text-color="#409EFF"
:unique-opened="true"
:collapse-transition="false"
@select="handleSelect">
<template v-for="menu in menus" :key="menu.menuId">
<el-sub-menu v-if="menu.children && menu.children.length" :index="menu.menuId.toString()">
<template #title>
<el-icon><component :is="menu.icon || 'Menu'" /></el-icon>
<span>{{ menu.menuName }}</span>
</template>
<el-menu-item
v-for="child in menu.children"
:key="child.menuId"
:index="child.menuId.toString()">
<el-icon><component :is="child.icon || 'Menu'" /></el-icon>
<template #title>{{ child.menuName }}</template>
</el-menu-item>
</el-sub-menu>
<el-menu-item v-else :index="menu.menuId.toString()">
<el-icon><component :is="menu.icon || 'Menu'" /></el-icon>
<template #title>{{ menu.menuName }}</template>
</el-menu-item>
</template>
</el-menu>
</div>
<main class="content">
<iframe :src="currentUrl" frameborder="0" style="width: 100%; height: 100%;"></iframe>
</main>
</div>
</div>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/element-plus"></script>
<script src="https://unpkg.com/@element-plus/icons-vue"></script>
<script>
const { createApp, ref, onMounted } = Vue
const app = createApp({
setup() {
const isCollapse = ref(false)
const activeMenu = ref('')
const currentUrl = ref('')
const menus = ref([])
const userInfo = ref({
userName: ''
})
const toggleSidebar = () => {
isCollapse.value = !isCollapse.value
}
const handleSelect = (index) => {
const findMenu = (menus, id) => {
for (const menu of menus) {
if (menu.menuId.toString() === id) return menu
if (menu.children) {
const found = findMenu(menu.children, id)
if (found) return found
}
}
return null
}
const menu = findMenu(menus.value, index)
if (menu && menu.url) {
currentUrl.value = menu.url
}
}
const handleCommand = (command) => {
switch (command) {
case 'logout':
fetch('/api/logout', { method: 'POST' })
.then(() => window.location.href = '/login')
break
case 'profile':
currentUrl.value = '/profile'
break
case 'password':
currentUrl.value = '/password'
break
}
}
onMounted(() => {
// 获取用户信息和菜单数据
Promise.all([
fetch('/api/user/info').then(res => res.json()),
fetch('/api/user/menus').then(res => res.json())
])
.then(([userRes, menuRes]) => {
if (userRes.code === 0) {
userInfo.value = userRes.data
}
if (menuRes.code === 0) {
menus.value = menuRes.data
if (menus.value.length > 0) {
const firstMenu = menus.value[0]
activeMenu.value = firstMenu.menuId.toString()
if (firstMenu.url) {
currentUrl.value = firstMenu.url
} else if (firstMenu.children && firstMenu.children.length > 0) {
currentUrl.value = firstMenu.children[0].url
activeMenu.value = firstMenu.children[0].menuId.toString()
}
}
}
})
.catch(error => {
ElementPlus.ElMessage.error('获取用户信息失败')
})
})
return {
isCollapse,
activeMenu,
currentUrl,
menus,
userInfo,
toggleSidebar,
handleSelect,
handleCommand
}
}
})
app.use(ElementPlus)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')
</script>
</body>
</html>

View File

@ -0,0 +1,94 @@
// 登录和权限管理相关的JavaScript代码
const auth = {
// 存储登录信息
setLoginInfo(loginResponse) {
localStorage.setItem('token', loginResponse.token);
localStorage.setItem('user', JSON.stringify(loginResponse.user));
localStorage.setItem('roles', JSON.stringify(loginResponse.roles));
localStorage.setItem('menus', JSON.stringify(loginResponse.menus));
},
// 清除登录信息
clearLoginInfo() {
localStorage.removeItem('token');
localStorage.removeItem('user');
localStorage.removeItem('roles');
localStorage.removeItem('menus');
},
// 获取用户信息
getUser() {
const userStr = localStorage.getItem('user');
return userStr ? JSON.parse(userStr) : null;
},
// 获取用户角色
getRoles() {
const rolesStr = localStorage.getItem('roles');
return rolesStr ? JSON.parse(rolesStr) : [];
},
// 获取用户菜单
getMenus() {
const menusStr = localStorage.getItem('menus');
return menusStr ? JSON.parse(menusStr) : [];
},
// 生成菜单树
generateMenuTree(menus) {
const menuMap = {};
const menuTree = [];
// 创建菜单映射
menus.forEach(menu => {
menuMap[menu.menuId] = {
...menu,
children: []
};
});
// 构建菜单树
menus.forEach(menu => {
const menuItem = menuMap[menu.menuId];
if (menu.parentId === 0) {
menuTree.push(menuItem);
} else {
const parentMenu = menuMap[menu.parentId];
if (parentMenu) {
parentMenu.children.push(menuItem);
}
}
});
return menuTree;
},
// 渲染菜单
renderMenu(menuTree) {
const menuHtml = menuTree.map(menu => {
if (menu.children && menu.children.length > 0) {
return `
<el-sub-menu index="${menu.menuId}">
<template #title>
<el-icon><component is="${menu.icon || 'Menu'}"></component></el-icon>
<span>${menu.menuName}</span>
</template>
${this.renderMenu(menu.children)}
</el-sub-menu>
`;
} else {
return `
<el-menu-item index="${menu.path}">
<el-icon><component is="${menu.icon || 'Document'}"></component></el-icon>
<span>${menu.menuName}</span>
</el-menu-item>
`;
}
}).join('');
return menuHtml;
}
};
// 导出auth对象
export default auth;

136
template/login.html 100644
View File

@ -0,0 +1,136 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录 - 售后服务平台</title>
<link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css">
<style>
body {
margin: 0;
padding: 0;
background-color: #f5f7fa;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.login-container {
width: 400px;
padding: 40px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.login-title {
text-align: center;
margin-bottom: 30px;
color: #303133;
font-size: 24px;
}
.captcha-container {
display: flex;
align-items: center;
gap: 10px;
}
.captcha-img {
height: 40px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="app">
<div class="login-container">
<h2 class="login-title">售后服务平台</h2>
<el-form :model="loginForm" :rules="rules" ref="loginFormRef">
<el-form-item prop="username">
<el-input v-model="loginForm.username" placeholder="请输入账号" prefix-icon="User"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" type="password" placeholder="请输入密码" prefix-icon="Lock" show-password></el-input>
</el-form-item>
<el-form-item prop="captcha">
<div class="captcha-container">
<el-input v-model="loginForm.captcha" placeholder="请输入验证码" style="flex: 1;"></el-input>
<img :src="captchaUrl" class="captcha-img" @click="refreshCaptcha" alt="验证码">
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" style="width: 100%" @click="handleLogin">登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/element-plus"></script>
<script src="https://unpkg.com/@element-plus/icons-vue"></script>
<script>
const { createApp, ref } = Vue
const app = createApp({
setup() {
const loginFormRef = ref(null)
const loginForm = ref({
username: '',
password: '',
captcha: ''
})
const captchaUrl = ref('/api/captcha')
const rules = {
username: [{ required: true, message: '请输入账号', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
captcha: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
}
const refreshCaptcha = () => {
captchaUrl.value = `/api/captcha?t=${new Date().getTime()}`
}
const handleLogin = () => {
loginFormRef.value.validate((valid) => {
if (valid) {
fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(loginForm.value)
})
.then(response => response.json())
.then(data => {
if (data.code === 0) {
window.location.href = '/'
} else {
ElementPlus.ElMessage.error(data.message)
refreshCaptcha()
}
})
.catch(error => {
ElementPlus.ElMessage.error('登录失败,请稍后重试')
refreshCaptcha()
})
}
})
}
return {
loginFormRef,
loginForm,
rules,
captchaUrl,
handleLogin,
refreshCaptcha
}
}
})
app.use(ElementPlus)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')
</script>
</body>
</html>