!14 存储桶改为private

* 后台 使用许可页面
* 优化:移除API访问地址配置
* 后台、pc、h5 删除无用配置
* docker部署优化
* 2.0 networkMode=bridge
* changelog
* 学员端权限为空报错
* h5 我的页面请求优化
* 缓存查询
* 后台 学员列表报错、线上课-上架时间字段优化
* 后台、pc、h5 使用签名地址
* 学员端接口修改
* 后台、pc 使用签名地址
* 后台 使用签名地址
* 上传接口
* 上传接口
* 系统配置
* 线上课封面
* bucket由public改为private
* 资源相关表实体及对象修改
* 统一数据库脚本
This commit is contained in:
白书科技
2025-05-22 07:23:06 +00:00
parent c206fa4bf2
commit 12daa31ab9
134 changed files with 10054 additions and 1231 deletions

3062
playedu-h5/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -106,28 +106,29 @@ const CoursePage = () => {
navigate(`/course/${cid}/hour/${id}`);
};
const downLoadFile = (cid: number, id: number) => {
const downLoadFile = (cid: number, id: number, rid: number) => {
vod.downloadAttachment(cid, id).then((res: any) => {
let url = res.data.resource_url[rid];
if (isWechat()) {
if (isIOS()) {
Toast.show("请点击右上角···浏览器打开下载");
}
var input = document.createElement("input");
input.value = res.data.download_url;
input.value = url;
document.body.appendChild(input);
input.select();
document.execCommand("Copy");
document.body.removeChild(input);
window.open(res.data.download_url);
window.open(url);
} else {
if (isIOS()) {
setDownLoadTemplateURL(res.data.download_url);
setDownLoadTemplateURL(url);
setTimeout(() => {
let $do: any = document.querySelector("#downLoadExcel");
$do.click();
}, 500);
} else {
window.open(res.data.download_url);
window.open(url);
}
}
});
@@ -287,7 +288,9 @@ const CoursePage = () => {
</div>
<div
className={styles["download"]}
onClick={() => downLoadFile(item.course_id, item.id)}
onClick={() =>
downLoadFile(item.course_id, item.id, item.rid)
}
>
</div>

View File

@@ -120,7 +120,7 @@ const CoursePlayPage = () => {
} else if (record && record.is_finished === 1) {
setWatchedSeconds(res.data.hour.duration);
}
getVideoUrl(params);
getVideoUrl(res.data.hour.rid, params);
setLoading(false);
})
.catch((e) => {
@@ -128,12 +128,12 @@ const CoursePlayPage = () => {
});
};
const getVideoUrl = (data: any) => {
const getVideoUrl = (rid: number, data: any) => {
Course.playUrl(Number(params.courseId), Number(params.hourId)).then(
(res: any) => {
window.player && window.player.destroy();
setPlayUrl(res.data.url);
initDPlayer(res.data.url, 0, data);
setPlayUrl(res.data.resource_url[rid]);
initDPlayer(res.data.resource_url[rid], 0, data);
}
);
};

View File

@@ -9,6 +9,9 @@ import { useNavigate, useLocation } from "react-router-dom";
import { Footer, Empty } from "../../components";
import { CoursesModel } from "./compenents/courses-model";
import { isEmptyObject } from "../../utils/index";
import defaultThumb1 from "../../assets/thumb/thumb1.png";
import defaultThumb2 from "../../assets/thumb/thumb2.png";
import defaultThumb3 from "../../assets/thumb/thumb3.png";
type LocalUserLearnHourRecordModel = {
[key: number]: UserLearnHourRecordModel;
@@ -36,6 +39,7 @@ const IndexPage = () => {
useState<LocalUserLearnHourRecordModel>({});
const [learnCourseHourCount, setLearnCourseHourCount] =
useState<LocalUserLearnHourCountModel>({});
const [resourceUrl, setResourceUrl] = useState<ResourceUrlModel>({});
const systemConfig = useSelector((state: any) => state.systemConfig.value);
const currentDepId = useSelector(
(state: any) => state.loginUser.value.currentDepId
@@ -85,6 +89,7 @@ const IndexPage = () => {
const records = res.data.learn_course_records;
setLearnCourseRecords(records);
setLearnCourseHourCount(res.data.user_course_hour_count);
setResourceUrl(res.data.resource_url);
if (Number(tabKey) === 0) {
setCoursesList(res.data.courses);
} else if (Number(tabKey) === 1) {
@@ -323,7 +328,15 @@ const IndexPage = () => {
<CoursesModel
id={item.id}
title={item.title}
thumb={item.thumb}
thumb={
item.thumb === -1
? defaultThumb1
: item.thumb === -2
? defaultThumb2
: item.thumb === -3
? defaultThumb3
: resourceUrl[item.thumb]
}
isRequired={item.is_required}
record={learnCourseRecords[item.id]}
hourCount={learnCourseHourCount[item.id]}

View File

@@ -25,11 +25,11 @@ export const InitPage = (props: Props) => {
let config: SystemConfigStoreInterface = {
//系统配置
"ldap-enabled": props.configData["ldap-enabled"],
systemApiUrl: props.configData["system-api-url"],
systemH5Url: props.configData["system-h5-url"],
systemLogo: props.configData["system-logo"],
systemName: props.configData["system-name"],
systemPcUrl: props.configData["system-pc-url"],
resourceUrl: props.configData["resource_url"],
pcIndexFooterMsg: props.configData["system-pc-index-footer-msg"],
//播放器配置
playerPoster: props.configData["player-poster"],

View File

@@ -87,11 +87,11 @@ const LoginPage = () => {
let config: SystemConfigStoreInterface = {
//系统配置
"ldap-enabled": configRes.data["ldap-enabled"],
systemApiUrl: configRes.data["system-api-url"],
systemH5Url: configRes.data["system-h5-url"],
systemLogo: configRes.data["system-logo"],
systemName: configRes.data["system-name"],
systemPcUrl: configRes.data["system-pc-url"],
resourceUrl: configRes.data["resource_url"],
pcIndexFooterMsg: configRes.data["system-pc-index-footer-msg"],
//播放器配置
playerPoster: configRes.data["player-poster"],

View File

@@ -8,6 +8,7 @@ import { ImageUploadItem } from "antd-mobile/es/components/image-uploader";
import styles from "./index.module.scss";
import { useDispatch, useSelector } from "react-redux";
import moreIcon from "../../assets/images/commen/icon-more.png";
import memberDefaultAvatar from "../../assets/thumb/avatar.png";
const MemberPage = () => {
const dispatch = useDispatch();
@@ -33,6 +34,9 @@ const MemberPage = () => {
const currentDepId = useSelector(
(state: any) => state.loginUser.value.currentDepId
);
const resourceUrl = useSelector(
(state: any) => state.loginUser.value.resourceUrl
);
useEffect(() => {
document.title = "我的";
@@ -50,30 +54,35 @@ const MemberPage = () => {
return;
}
getData();
}, [currentDepId, user]);
}, [currentDepId]);
const getData = () => {
setLoading(true);
member.courses(currentDepId, 0).then((res: any) => {
setStats(res.data.stats);
let todayData = studyTimeFormat(res.data.stats.today_learn_duration);
if (todayData) {
setLearnTodayHour(todayData[0]);
setLearnTodayMin(todayData[1]);
if (todayData[1] === 0 && todayData[2] > 0) {
setLearnTodayMin(1);
member
.courses(currentDepId, 0)
.then((res: any) => {
setStats(res.data.stats);
let todayData = studyTimeFormat(res.data.stats.today_learn_duration);
if (todayData) {
setLearnTodayHour(todayData[0]);
setLearnTodayMin(todayData[1]);
if (todayData[1] === 0 && todayData[2] > 0) {
setLearnTodayMin(1);
}
}
}
let totalData = studyTimeFormat(res.data.stats.learn_duration);
if (totalData) {
setLearnTotalHour(totalData[0]);
setLearnTotalMin(totalData[1]);
if (totalData[1] === 0 && totalData[2] > 0) {
setLearnTodayMin(1);
let totalData = studyTimeFormat(res.data.stats.learn_duration);
if (totalData) {
setLearnTotalHour(totalData[0]);
setLearnTotalMin(totalData[1]);
if (totalData[1] === 0 && totalData[2] > 0) {
setLearnTodayMin(1);
}
}
}
setLoading(false);
});
setLoading(false);
})
.catch((e) => {
setLoading(false);
});
};
const setClick = () => {
@@ -157,16 +166,22 @@ const MemberPage = () => {
)}
{init && (
<>
<Image
width={100}
height={100}
style={{
borderRadius: "50%",
marginRight: 20,
}}
fit="cover"
src={user?.avatar}
/>
{user ? (
<Image
width={100}
height={100}
style={{
borderRadius: "50%",
marginRight: 20,
}}
fit="cover"
src={
user.avatar === -1
? memberDefaultAvatar
: resourceUrl[user.avatar]
}
/>
) : null}
<div className={styles["other-cont"]}>
<div className={styles["name"]}>{user?.name}</div>
<div className={styles["departments"]}>

View File

@@ -5,6 +5,9 @@ import styles from "./index.module.scss";
import { course } from "../../api/index";
import { Empty } from "../../components";
import { CoursesModel } from "./compenents/courses-model";
import defaultThumb1 from "../../assets/thumb/thumb1.png";
import defaultThumb2 from "../../assets/thumb/thumb2.png";
import defaultThumb3 from "../../assets/thumb/thumb3.png";
import moment from "moment";
const StudyPage = () => {
@@ -12,6 +15,7 @@ const StudyPage = () => {
const [todayCourses, setTodayCourses] = useState<CourseModel[]>([]);
const [yesterdayCourses, setYesterdayCourses] = useState<CourseModel[]>([]);
const [courses, setCourses] = useState<CourseModel[]>([]);
const [resourceUrl, setResourceUrl] = useState<ResourceUrlModel>({});
useEffect(() => {
document.title = "最近学习";
@@ -26,29 +30,32 @@ const StudyPage = () => {
course
.latestLearn()
.then((res: any) => {
let data = res.data;
let today: CourseModel[] = [];
let yesterday: CourseModel[] = [];
let box: CourseModel[] = [];
if (data && data.length > 0) {
data.map((item: any) => {
let time = moment(item.hour_record.updated_at)
.utcOffset(0)
.format("YYYY-MM-DD HH:mm:ss");
if (moment(time).isSame(moment(), "day")) {
today.push(item);
} else if (
moment(time).isSame(moment().subtract(1, "day"), "day")
) {
yesterday.push(item);
} else {
box.push(item);
}
});
if (res.data.resource_url && res.data.user_latest_learns) {
setResourceUrl(res.data.resource_url);
let data = res.data.user_latest_learns;
let today: CourseModel[] = [];
let yesterday: CourseModel[] = [];
let box: CourseModel[] = [];
if (data && data.length > 0) {
data.map((item: any) => {
let time = moment(item.hour_record.updated_at)
.utcOffset(0)
.format("YYYY-MM-DD HH:mm:ss");
if (moment(time).isSame(moment(), "day")) {
today.push(item);
} else if (
moment(time).isSame(moment().subtract(1, "day"), "day")
) {
yesterday.push(item);
} else {
box.push(item);
}
});
}
setTodayCourses(today);
setYesterdayCourses(yesterday);
setCourses(box);
}
setTodayCourses(today);
setYesterdayCourses(yesterday);
setCourses(box);
setLoading(false);
})
.catch((e) => {
@@ -101,7 +108,15 @@ const StudyPage = () => {
<CoursesModel
id={item.course.id}
title={item.course.title}
thumb={item.course.thumb}
thumb={
item.course.thumb === -1
? defaultThumb1
: item.course.thumb === -2
? defaultThumb2
: item.course.thumb === -3
? defaultThumb3
: resourceUrl[item.course.thumb]
}
isRequired={item.course.is_required}
record={item.record}
></CoursesModel>
@@ -119,7 +134,15 @@ const StudyPage = () => {
<CoursesModel
id={item.course.id}
title={item.course.title}
thumb={item.course.thumb}
thumb={
item.course.thumb === -1
? defaultThumb1
: item.course.thumb === -2
? defaultThumb2
: item.course.thumb === -3
? defaultThumb3
: resourceUrl[item.course.thumb]
}
isRequired={item.course.is_required}
record={item.record}
></CoursesModel>
@@ -137,7 +160,15 @@ const StudyPage = () => {
<CoursesModel
id={item.course.id}
title={item.course.title}
thumb={item.course.thumb}
thumb={
item.course.thumb === -1
? defaultThumb1
: item.course.thumb === -2
? defaultThumb2
: item.course.thumb === -3
? defaultThumb3
: resourceUrl[item.course.thumb]
}
isRequired={item.course.is_required}
record={item.record}
></CoursesModel>

View File

@@ -2,7 +2,7 @@ declare global {
interface CourseModel {
id: number;
title: string;
thumb: string;
thumb: number;
short_desc: string;
is_required: number;
charge: number;
@@ -57,7 +57,7 @@ declare global {
interface UserModel {
id: number;
name: string;
avatar: string;
avatar: number;
credit1: number;
email: string;
create_city: string;
@@ -72,6 +72,10 @@ declare global {
login_at?: string;
verify_at?: string;
}
interface ResourceUrlModel {
[key: number]: string;
}
}
export {};

View File

@@ -2,7 +2,6 @@ import { createSlice } from "@reduxjs/toolkit";
type SystemConfigStoreInterface = {
"ldap-enabled": string;
systemApiUrl: string;
systemPcUrl: string;
systemH5Url: string;
systemLogo: string;
@@ -14,11 +13,11 @@ type SystemConfigStoreInterface = {
playerBulletSecretText: string;
playerBulletSecretColor: string;
playerBulletSecretOpacity: string;
resourceUrl?: ResourceUrlModel;
};
let defaultValue: SystemConfigStoreInterface = {
"ldap-enabled": "",
systemApiUrl: "",
systemPcUrl: "",
systemH5Url: "",
systemLogo: "",
@@ -30,6 +29,7 @@ let defaultValue: SystemConfigStoreInterface = {
playerBulletSecretText: "",
playerBulletSecretColor: "",
playerBulletSecretOpacity: "",
resourceUrl: {},
};
const systemConfigSlice = createSlice({

View File

@@ -11,6 +11,7 @@ type UserStoreInterface = {
user: UserModel | null;
departments: string[];
currentDepId: number;
resourceUrl: ResourceUrlModel;
isLogin: boolean;
};
@@ -18,6 +19,7 @@ let defaultValue: UserStoreInterface = {
user: null,
departments: [],
currentDepId: Number(getDepKey()) || 0,
resourceUrl: {},
isLogin: false,
};
@@ -30,6 +32,7 @@ const loginUserSlice = createSlice({
loginAction(stage, e) {
stage.value.user = e.payload.user;
stage.value.departments = e.payload.departments;
stage.value.resourceUrl = e.payload.resource_url;
stage.value.isLogin = true;
if (e.payload.departments.length > 0 && stage.value.currentDepId === 0) {
stage.value.currentDepId = e.payload.departments[0].id;
@@ -39,6 +42,7 @@ const loginUserSlice = createSlice({
logoutAction(stage) {
stage.value.user = null;
stage.value.departments = [];
stage.value.resourceUrl = {};
stage.value.isLogin = false;
stage.value.currentDepId = 0;
clearToken();