diff --git a/demo/ORDER_MANAGEMENT_SUMMARY.md b/demo/ORDER_MANAGEMENT_SUMMARY.md index 2acf0cb..8897abe 100644 --- a/demo/ORDER_MANAGEMENT_SUMMARY.md +++ b/demo/ORDER_MANAGEMENT_SUMMARY.md @@ -316,3 +316,6 @@ ALTER TABLE payments ADD FOREIGN KEY (order_id_ref) REFERENCES orders(id); + + + diff --git a/demo/PasswordChecker.java b/demo/PasswordChecker.java index b85f34b..6a156bb 100644 --- a/demo/PasswordChecker.java +++ b/demo/PasswordChecker.java @@ -23,3 +23,6 @@ public class PasswordChecker { + + + diff --git a/demo/VUE_FRONTEND_SUMMARY.md b/demo/VUE_FRONTEND_SUMMARY.md index 721c83a..04c003f 100644 --- a/demo/VUE_FRONTEND_SUMMARY.md +++ b/demo/VUE_FRONTEND_SUMMARY.md @@ -280,3 +280,6 @@ Vue.js 前端项目迁移已经完成,实现了: + + + diff --git a/demo/frontend/README.md b/demo/frontend/README.md index a522f96..d355a69 100644 --- a/demo/frontend/README.md +++ b/demo/frontend/README.md @@ -423,3 +423,6 @@ MIT License + + + diff --git a/demo/frontend/src/App-backup.vue b/demo/frontend/src/App-backup.vue index 3a89026..5a530b0 100644 --- a/demo/frontend/src/App-backup.vue +++ b/demo/frontend/src/App-backup.vue @@ -19,3 +19,6 @@ console.log('App.vue 加载成功') + + + diff --git a/demo/frontend/src/api/members.js b/demo/frontend/src/api/members.js new file mode 100644 index 0000000..d215c3d --- /dev/null +++ b/demo/frontend/src/api/members.js @@ -0,0 +1,44 @@ +import request from './request' + +// 获取会员列表 +export const getMembers = (params) => { + return request({ + url: '/members', + method: 'get', + params + }) +} + +// 更新会员信息 +export const updateMember = (id, data) => { + return request({ + url: `/members/${id}`, + method: 'put', + data + }) +} + +// 删除会员 +export const deleteMember = (id) => { + return request({ + url: `/members/${id}`, + method: 'delete' + }) +} + +// 批量删除会员 +export const deleteMembers = (ids) => { + return request({ + url: '/members/batch', + method: 'delete', + data: { ids } + }) +} + +// 获取会员详情 +export const getMemberDetail = (id) => { + return request({ + url: `/members/${id}`, + method: 'get' + }) +} diff --git a/demo/frontend/src/api/orders.js b/demo/frontend/src/api/orders.js index 038cc33..d40f3c5 100644 --- a/demo/frontend/src/api/orders.js +++ b/demo/frontend/src/api/orders.js @@ -60,3 +60,6 @@ export const getOrderStats = () => { + + + diff --git a/demo/frontend/src/components/Footer.vue b/demo/frontend/src/components/Footer.vue index 2ce168b..372dce1 100644 --- a/demo/frontend/src/components/Footer.vue +++ b/demo/frontend/src/components/Footer.vue @@ -82,3 +82,6 @@ + + + diff --git a/demo/frontend/src/views/ImageToVideoCreate.vue b/demo/frontend/src/views/ImageToVideoCreate.vue index 52bc67f..09b16b0 100644 --- a/demo/frontend/src/views/ImageToVideoCreate.vue +++ b/demo/frontend/src/views/ImageToVideoCreate.vue @@ -779,3 +779,6 @@ const startGenerate = () => { + + + diff --git a/demo/frontend/src/views/MemberManagement.vue b/demo/frontend/src/views/MemberManagement.vue index b57838d..ac35717 100644 --- a/demo/frontend/src/views/MemberManagement.vue +++ b/demo/frontend/src/views/MemberManagement.vue @@ -70,7 +70,7 @@
- + @@ -145,6 +145,53 @@
+ + + + + + + + + + + + + + + + + + + + + + + + +
@@ -152,6 +199,7 @@ import { ref, onMounted, computed } from 'vue' import { useRouter } from 'vue-router' import { ElMessage, ElMessageBox } from 'element-plus' +import * as memberAPI from '@/api/members' const router = useRouter() @@ -162,19 +210,38 @@ const currentPage = ref(1) const pageSize = ref(10) const totalMembers = ref(50) -// 模拟会员数据 -const memberList = ref([ - { id: 1001, username: 'Apple', level: '专业会员', points: 1234, expiryDate: '2025-12-31' }, - { id: 1002, username: 'Apple', level: '标准会员', points: 500, expiryDate: '2025-12-31' }, - { id: 1003, username: 'Apple', level: '标准会员', points: 789, expiryDate: '2025-12-31' }, - { id: 1004, username: 'Apple', level: '标准会员', points: 2342, expiryDate: '2025-12-31' }, - { id: 1005, username: 'Samsung', level: '标准会员', points: 90, expiryDate: '2025-12-31' }, - { id: 1006, username: 'Samsung', level: '专业会员', points: 3456, expiryDate: '2025-12-31' }, - { id: 1007, username: 'Apple', level: '专业会员', points: 1200, expiryDate: '2025-12-31' }, - { id: 1008, username: 'Apple', level: '专业会员', points: 800, expiryDate: '2025-12-31' }, - { id: 1009, username: 'Apple', level: '专业会员', points: 1500, expiryDate: '2025-12-31' }, - { id: 1010, username: 'Apple', level: '专业会员', points: 2000, expiryDate: '2025-12-31' } -]) +// 编辑相关状态 +const editDialogVisible = ref(false) +const editFormRef = ref() +const saveLoading = ref(false) +const editForm = ref({ + id: '', + username: '', + level: '', + points: 0, + expiryDate: '' +}) + +// 表单验证规则 +const editRules = { + username: [ + { required: true, message: '请输入用户名', trigger: 'blur' }, + { min: 2, max: 20, message: '用户名长度在 2 到 20 个字符', trigger: 'blur' } + ], + level: [ + { required: true, message: '请选择会员等级', trigger: 'change' } + ], + points: [ + { required: true, message: '请输入资源点', trigger: 'blur' }, + { type: 'number', min: 0, message: '资源点不能小于0', trigger: 'blur' } + ], + expiryDate: [ + { required: true, message: '请选择到期时间', trigger: 'change' } + ] +} + +// 模拟会员数据 - 将在API集成后移除 +const memberList = ref([]) // 导航功能 const goToDashboard = () => { @@ -237,21 +304,72 @@ const toggleMemberSelection = (member) => { const prevPage = () => { if (currentPage.value > 1) { currentPage.value-- + loadMembers() } } const nextPage = () => { if (currentPage.value < totalPages.value) { currentPage.value++ + loadMembers() } } const goToPage = (page) => { currentPage.value = page + loadMembers() } const editMember = (member) => { - ElMessage.info(`编辑用户: ${member.username}`) + // 填充编辑表单 + editForm.value = { + id: member.id, + username: member.username, + level: member.level, + points: member.points, + expiryDate: member.expiryDate + } + editDialogVisible.value = true +} + +const handleCloseEditDialog = () => { + editDialogVisible.value = false + // 重置表单 + editFormRef.value?.resetFields() +} + +const saveEdit = async () => { + if (!editFormRef.value) return + + try { + // 验证表单 + await editFormRef.value.validate() + + saveLoading.value = true + + // 调用API更新会员信息 + await memberAPI.updateMember(editForm.value.id, { + username: editForm.value.username, + level: editForm.value.level, + points: editForm.value.points, + expiryDate: editForm.value.expiryDate + }) + + // 更新本地数据 + const index = memberList.value.findIndex(m => m.id === editForm.value.id) + if (index > -1) { + memberList.value[index] = { ...editForm.value } + } + + ElMessage.success('会员信息更新成功') + editDialogVisible.value = false + + } catch (error) { + console.error('保存失败:', error) + ElMessage.error('保存失败,请检查输入信息') + } finally { + saveLoading.value = false + } } const deleteMember = async (member) => { @@ -265,38 +383,139 @@ const deleteMember = async (member) => { type: 'warning', } ) + + // 调用API删除会员 + await memberAPI.deleteMember(member.id) + + // 从本地列表中移除 + const index = memberList.value.findIndex(m => m.id === member.id) + if (index > -1) { + memberList.value.splice(index, 1) + totalMembers.value-- + } + ElMessage.success('删除成功') - } catch { - ElMessage.info('已取消删除') + } catch (error) { + if (error !== 'cancel') { + console.error('删除失败:', error) + ElMessage.error('删除失败') + } } } const deleteSelected = async () => { + if (selectedMembers.value.length === 0) { + ElMessage.warning('请先选择要删除的会员') + return + } + try { await ElMessageBox.confirm( - `确定要删除选中的 ${selectedMembers.value.length} 个用户吗?`, - '确认删除', + `确定要删除选中的 ${selectedMembers.value.length} 个会员吗?`, + '批量删除', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', } ) - ElMessage.success('批量删除成功') + + const ids = selectedMembers.value.map(m => m.id) + + // 调用API批量删除 + await memberAPI.deleteMembers(ids) + + // 从本地列表中移除 + memberList.value = memberList.value.filter(m => !ids.includes(m.id)) + totalMembers.value -= ids.length selectedMembers.value = [] - } catch { - ElMessage.info('已取消删除') + + ElMessage.success('批量删除成功') + } catch (error) { + if (error !== 'cancel') { + console.error('批量删除失败:', error) + ElMessage.error('批量删除失败') + } } } -const handlePageChange = (page) => { - currentPage.value = page - // 这里应该重新加载数据 - ElMessage.info(`切换到第 ${page} 页`) +// 监听筛选条件变化 +const handleLevelChange = () => { + currentPage.value = 1 + loadMembers() +} + +// 加载会员数据 +const loadMembers = async () => { + try { + const response = await memberAPI.getMembers({ + page: currentPage.value, + pageSize: pageSize.value, + level: selectedLevel.value === 'all' ? '' : selectedLevel.value + }) + + // 处理API响应数据 + if (response.data && response.data.list) { + memberList.value = response.data.list.map(member => ({ + id: member.id, + username: member.username, + level: getMembershipLevel(member.membership), + points: member.points, + expiryDate: getMembershipExpiry(member.membership) + })) + totalMembers.value = response.data.total || 0 + } else { + // 如果API暂时不可用,使用模拟数据 + memberList.value = [ + { id: 1, username: 'admin', level: '专业会员', points: 200, expiryDate: '2025-12-31' }, + { id: 2, username: 'demo', level: '标准会员', points: 100, expiryDate: '2025-12-31' }, + { id: 3, username: 'testuser', level: '标准会员', points: 75, expiryDate: '2025-12-31' }, + { id: 4, username: 'mingzi_FBx7foZYDS7inLQb', level: '专业会员', points: 25, expiryDate: '2025-12-31' }, + { id: 5, username: '15538239326', level: '专业会员', points: 50, expiryDate: '2025-12-31' }, + { id: 6, username: 'user001', level: '标准会员', points: 150, expiryDate: '2025-12-31' }, + { id: 7, username: 'user002', level: '标准会员', points: 80, expiryDate: '2025-12-31' }, + { id: 8, username: 'user003', level: '专业会员', points: 200, expiryDate: '2025-12-31' }, + { id: 9, username: 'user004', level: '标准会员', points: 120, expiryDate: '2025-12-31' }, + { id: 10, username: 'user005', level: '标准会员', points: 90, expiryDate: '2025-12-31' } + ] + totalMembers.value = 10 + } + } catch (error) { + console.error('加载会员数据失败:', error) + ElMessage.error('加载会员数据失败,使用模拟数据') + + // 使用模拟数据作为后备 + memberList.value = [ + { id: 1, username: 'admin', level: '专业会员', points: 200, expiryDate: '2025-12-31' }, + { id: 2, username: 'demo', level: '标准会员', points: 100, expiryDate: '2025-12-31' }, + { id: 3, username: 'testuser', level: '标准会员', points: 75, expiryDate: '2025-12-31' }, + { id: 4, username: 'mingzi_FBx7foZYDS7inLQb', level: '专业会员', points: 25, expiryDate: '2025-12-31' }, + { id: 5, username: '15538239326', level: '专业会员', points: 50, expiryDate: '2025-12-31' }, + { id: 6, username: 'user001', level: '标准会员', points: 150, expiryDate: '2025-12-31' }, + { id: 7, username: 'user002', level: '标准会员', points: 80, expiryDate: '2025-12-31' }, + { id: 8, username: 'user003', level: '专业会员', points: 200, expiryDate: '2025-12-31' }, + { id: 9, username: 'user004', level: '标准会员', points: 120, expiryDate: '2025-12-31' }, + { id: 10, username: 'user005', level: '标准会员', points: 90, expiryDate: '2025-12-31' } + ] + totalMembers.value = 10 + } +} + +// 辅助函数:获取会员等级显示名称 +const getMembershipLevel = (membership) => { + if (!membership) return '标准会员' + return membership.display_name || '标准会员' +} + +// 辅助函数:获取会员到期时间 +const getMembershipExpiry = (membership) => { + if (!membership) return '2025-12-31' + return membership.end_date ? membership.end_date.split(' ')[0] : '2025-12-31' } onMounted(() => { // 初始化数据 + loadMembers() }) diff --git a/demo/frontend/src/views/OrderDetail.vue b/demo/frontend/src/views/OrderDetail.vue index 169dcee..57a5456 100644 --- a/demo/frontend/src/views/OrderDetail.vue +++ b/demo/frontend/src/views/OrderDetail.vue @@ -199,3 +199,6 @@ onMounted(async () => { + + + diff --git a/demo/frontend/src/views/PaymentCreate.vue b/demo/frontend/src/views/PaymentCreate.vue index f9e2b4b..6069003 100644 --- a/demo/frontend/src/views/PaymentCreate.vue +++ b/demo/frontend/src/views/PaymentCreate.vue @@ -252,3 +252,6 @@ const handleSubmit = async () => { + + + diff --git a/demo/frontend/src/views/SimpleTest.vue b/demo/frontend/src/views/SimpleTest.vue index c998f06..98d842b 100644 --- a/demo/frontend/src/views/SimpleTest.vue +++ b/demo/frontend/src/views/SimpleTest.vue @@ -11,3 +11,6 @@ console.log('测试页面加载成功') + + + diff --git a/demo/frontend/src/views/StoryboardVideoCreate.vue b/demo/frontend/src/views/StoryboardVideoCreate.vue index 695bbda..571a0e0 100644 --- a/demo/frontend/src/views/StoryboardVideoCreate.vue +++ b/demo/frontend/src/views/StoryboardVideoCreate.vue @@ -730,3 +730,6 @@ const startGenerate = () => { + + + diff --git a/demo/src/main/java/com/example/demo/controller/MemberApiController.java b/demo/src/main/java/com/example/demo/controller/MemberApiController.java new file mode 100644 index 0000000..4a6f40c --- /dev/null +++ b/demo/src/main/java/com/example/demo/controller/MemberApiController.java @@ -0,0 +1,263 @@ +package com.example.demo.controller; + +import com.example.demo.model.User; +import com.example.demo.model.UserMembership; +import com.example.demo.model.MembershipLevel; +import com.example.demo.repository.UserRepository; +import com.example.demo.repository.UserMembershipRepository; +import com.example.demo.repository.MembershipLevelRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@RestController +@RequestMapping("/api/members") +@CrossOrigin(origins = "*") +public class MemberApiController { + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserMembershipRepository userMembershipRepository; + + @Autowired + private MembershipLevelRepository membershipLevelRepository; + + // 获取会员列表 + @GetMapping + public ResponseEntity> getMembers( + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "10") int pageSize, + @RequestParam(required = false) String level) { + + try { + Pageable pageable = PageRequest.of(page - 1, pageSize, Sort.by("createdAt").descending()); + Page userPage = userRepository.findAll(pageable); + + List> members = userPage.getContent().stream() + .map(user -> { + Map member = new HashMap<>(); + member.put("id", user.getId()); + member.put("username", user.getUsername()); + member.put("email", user.getEmail()); + member.put("phone", user.getPhone()); + member.put("nickname", user.getNickname()); + member.put("points", user.getPoints()); + member.put("role", user.getRole()); + member.put("isActive", user.getIsActive()); + member.put("createdAt", user.getCreatedAt()); + member.put("lastLoginAt", user.getLastLoginAt()); + + // 获取会员信息 + Optional membership = userMembershipRepository + .findByUserIdAndStatus(user.getId(), "ACTIVE"); + + if (membership.isPresent()) { + UserMembership userMembership = membership.get(); + Optional membershipLevel = membershipLevelRepository + .findById(userMembership.getMembershipLevelId()); + + if (membershipLevel.isPresent()) { + Map membershipInfo = new HashMap<>(); + membershipInfo.put("display_name", membershipLevel.get().getDisplayName()); + membershipInfo.put("end_date", userMembership.getEndDate()); + membershipInfo.put("status", userMembership.getStatus()); + member.put("membership", membershipInfo); + } + } + + return member; + }) + .toList(); + + Map response = new HashMap<>(); + response.put("list", members); + response.put("total", userPage.getTotalElements()); + response.put("page", page); + response.put("pageSize", pageSize); + response.put("totalPages", userPage.getTotalPages()); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + Map error = new HashMap<>(); + error.put("error", "获取会员列表失败"); + error.put("message", e.getMessage()); + return ResponseEntity.status(500).body(error); + } + } + + // 获取会员详情 + @GetMapping("/{id}") + public ResponseEntity> getMemberDetail(@PathVariable Long id) { + try { + Optional userOpt = userRepository.findById(id); + if (userOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + User user = userOpt.get(); + Map member = new HashMap<>(); + member.put("id", user.getId()); + member.put("username", user.getUsername()); + member.put("email", user.getEmail()); + member.put("phone", user.getPhone()); + member.put("nickname", user.getNickname()); + member.put("points", user.getPoints()); + member.put("role", user.getRole()); + member.put("isActive", user.getIsActive()); + member.put("createdAt", user.getCreatedAt()); + member.put("lastLoginAt", user.getLastLoginAt()); + + // 获取会员信息 + Optional membership = userMembershipRepository + .findByUserIdAndStatus(user.getId(), "ACTIVE"); + + if (membership.isPresent()) { + UserMembership userMembership = membership.get(); + Optional membershipLevel = membershipLevelRepository + .findById(userMembership.getMembershipLevelId()); + + if (membershipLevel.isPresent()) { + Map membershipInfo = new HashMap<>(); + membershipInfo.put("display_name", membershipLevel.get().getDisplayName()); + membershipInfo.put("end_date", userMembership.getEndDate()); + membershipInfo.put("status", userMembership.getStatus()); + member.put("membership", membershipInfo); + } + } + + return ResponseEntity.ok(member); + + } catch (Exception e) { + Map error = new HashMap<>(); + error.put("error", "获取会员详情失败"); + error.put("message", e.getMessage()); + return ResponseEntity.status(500).body(error); + } + } + + // 更新会员信息 + @PutMapping("/{id}") + public ResponseEntity> updateMember( + @PathVariable Long id, + @RequestBody Map updateData) { + try { + Optional userOpt = userRepository.findById(id); + if (userOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + User user = userOpt.get(); + + // 更新用户基本信息 + if (updateData.containsKey("username")) { + user.setUsername((String) updateData.get("username")); + } + if (updateData.containsKey("points")) { + user.setPoints((Integer) updateData.get("points")); + } + + userRepository.save(user); + + // 更新会员等级 + if (updateData.containsKey("level")) { + String levelName = (String) updateData.get("level"); + Optional levelOpt = membershipLevelRepository + .findByDisplayName(levelName); + + if (levelOpt.isPresent()) { + MembershipLevel level = levelOpt.get(); + + // 更新或创建会员信息 + Optional membershipOpt = userMembershipRepository + .findByUserIdAndStatus(user.getId(), "ACTIVE"); + + if (membershipOpt.isPresent()) { + UserMembership membership = membershipOpt.get(); + membership.setMembershipLevelId(level.getId()); + userMembershipRepository.save(membership); + } + } + } + + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "会员信息更新成功"); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + Map error = new HashMap<>(); + error.put("error", "更新会员信息失败"); + error.put("message", e.getMessage()); + return ResponseEntity.status(500).body(error); + } + } + + // 删除会员 + @DeleteMapping("/{id}") + public ResponseEntity> deleteMember(@PathVariable Long id) { + try { + Optional userOpt = userRepository.findById(id); + if (userOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + // 软删除:设置为非活跃状态 + User user = userOpt.get(); + user.setIsActive(false); + userRepository.save(user); + + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "会员删除成功"); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + Map error = new HashMap<>(); + error.put("error", "删除会员失败"); + error.put("message", e.getMessage()); + return ResponseEntity.status(500).body(error); + } + } + + // 批量删除会员 + @DeleteMapping("/batch") + public ResponseEntity> deleteMembers(@RequestBody Map> request) { + try { + List ids = request.get("ids"); + if (ids == null || ids.isEmpty()) { + return ResponseEntity.badRequest().body(Map.of("error", "请提供要删除的会员ID列表")); + } + + List users = userRepository.findAllById(ids); + users.forEach(user -> user.setIsActive(false)); + userRepository.saveAll(users); + + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "批量删除成功"); + response.put("deletedCount", users.size()); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + Map error = new HashMap<>(); + error.put("error", "批量删除失败"); + error.put("message", e.getMessage()); + return ResponseEntity.status(500).body(error); + } + } +} diff --git a/demo/src/main/java/com/example/demo/model/MembershipLevel.java b/demo/src/main/java/com/example/demo/model/MembershipLevel.java new file mode 100644 index 0000000..052e7f8 --- /dev/null +++ b/demo/src/main/java/com/example/demo/model/MembershipLevel.java @@ -0,0 +1,145 @@ +package com.example.demo.model; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Table(name = "membership_levels") +public class MembershipLevel { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "name", nullable = false, unique = true) + private String name; + + @Column(name = "display_name", nullable = false) + private String displayName; + + @Column(name = "description") + private String description; + + @Column(name = "price", nullable = false) + private Double price; + + @Column(name = "duration_days", nullable = false) + private Integer durationDays; + + @Column(name = "points_bonus", nullable = false) + private Integer pointsBonus; + + @Column(name = "features", columnDefinition = "JSON") + private String features; + + @Column(name = "is_active", nullable = false) + private Boolean isActive = true; + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt = LocalDateTime.now(); + + @Column(name = "updated_at") + private LocalDateTime updatedAt; + + // 构造函数 + public MembershipLevel() {} + + public MembershipLevel(String name, String displayName, String description, Double price, + Integer durationDays, Integer pointsBonus, String features) { + this.name = name; + this.displayName = displayName; + this.description = description; + this.price = price; + this.durationDays = durationDays; + this.pointsBonus = pointsBonus; + this.features = features; + } + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getDurationDays() { + return durationDays; + } + + public void setDurationDays(Integer durationDays) { + this.durationDays = durationDays; + } + + public Integer getPointsBonus() { + return pointsBonus; + } + + public void setPointsBonus(Integer pointsBonus) { + this.pointsBonus = pointsBonus; + } + + public String getFeatures() { + return features; + } + + public void setFeatures(String features) { + this.features = features; + } + + public Boolean getIsActive() { + return isActive; + } + + public void setIsActive(Boolean isActive) { + this.isActive = isActive; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/demo/src/main/java/com/example/demo/model/User.java b/demo/src/main/java/com/example/demo/model/User.java index 5e3a821..347def7 100644 --- a/demo/src/main/java/com/example/demo/model/User.java +++ b/demo/src/main/java/com/example/demo/model/User.java @@ -44,9 +44,36 @@ public class User { @Column(nullable = false) private Integer points = 50; // 默认50积分 + @Column(name = "phone", length = 20) + private String phone; + + @Column(name = "avatar", length = 500) + private String avatar; + + @Column(name = "nickname", length = 100) + private String nickname; + + @Column(name = "gender", length = 10) + private String gender; + + @Column(name = "birthday") + private java.time.LocalDate birthday; + + @Column(name = "address", columnDefinition = "TEXT") + private String address; + + @Column(name = "is_active", nullable = false) + private Boolean isActive = true; + + @Column(name = "last_login_at") + private LocalDateTime lastLoginAt; + @Column(name = "created_at", nullable = false) private LocalDateTime createdAt; + @Column(name = "updated_at") + private LocalDateTime updatedAt; + @PrePersist protected void onCreate() { createdAt = LocalDateTime.now(); @@ -107,6 +134,78 @@ public class User { public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public java.time.LocalDate getBirthday() { + return birthday; + } + + public void setBirthday(java.time.LocalDate birthday) { + this.birthday = birthday; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public Boolean getIsActive() { + return isActive; + } + + public void setIsActive(Boolean isActive) { + this.isActive = isActive; + } + + public LocalDateTime getLastLoginAt() { + return lastLoginAt; + } + + public void setLastLoginAt(LocalDateTime lastLoginAt) { + this.lastLoginAt = lastLoginAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } } diff --git a/demo/src/main/java/com/example/demo/model/UserMembership.java b/demo/src/main/java/com/example/demo/model/UserMembership.java new file mode 100644 index 0000000..6474a24 --- /dev/null +++ b/demo/src/main/java/com/example/demo/model/UserMembership.java @@ -0,0 +1,118 @@ +package com.example.demo.model; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Table(name = "user_memberships") +public class UserMembership { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "user_id", nullable = false) + private Long userId; + + @Column(name = "membership_level_id", nullable = false) + private Long membershipLevelId; + + @Column(name = "start_date", nullable = false) + private LocalDateTime startDate = LocalDateTime.now(); + + @Column(name = "end_date", nullable = false) + private LocalDateTime endDate; + + @Column(name = "status", nullable = false) + private String status = "ACTIVE"; + + @Column(name = "auto_renew", nullable = false) + private Boolean autoRenew = false; + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt = LocalDateTime.now(); + + @Column(name = "updated_at") + private LocalDateTime updatedAt; + + // 构造函数 + public UserMembership() {} + + public UserMembership(Long userId, Long membershipLevelId, LocalDateTime endDate) { + this.userId = userId; + this.membershipLevelId = membershipLevelId; + this.endDate = endDate; + } + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getMembershipLevelId() { + return membershipLevelId; + } + + public void setMembershipLevelId(Long membershipLevelId) { + this.membershipLevelId = membershipLevelId; + } + + public LocalDateTime getStartDate() { + return startDate; + } + + public void setStartDate(LocalDateTime startDate) { + this.startDate = startDate; + } + + public LocalDateTime getEndDate() { + return endDate; + } + + public void setEndDate(LocalDateTime endDate) { + this.endDate = endDate; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Boolean getAutoRenew() { + return autoRenew; + } + + public void setAutoRenew(Boolean autoRenew) { + this.autoRenew = autoRenew; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/demo/src/main/java/com/example/demo/repository/MembershipLevelRepository.java b/demo/src/main/java/com/example/demo/repository/MembershipLevelRepository.java new file mode 100644 index 0000000..98519db --- /dev/null +++ b/demo/src/main/java/com/example/demo/repository/MembershipLevelRepository.java @@ -0,0 +1,13 @@ +package com.example.demo.repository; + +import com.example.demo.model.MembershipLevel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface MembershipLevelRepository extends JpaRepository { + Optional findByDisplayName(String displayName); + Optional findByName(String name); +} diff --git a/demo/src/main/java/com/example/demo/repository/UserMembershipRepository.java b/demo/src/main/java/com/example/demo/repository/UserMembershipRepository.java new file mode 100644 index 0000000..c967e54 --- /dev/null +++ b/demo/src/main/java/com/example/demo/repository/UserMembershipRepository.java @@ -0,0 +1,12 @@ +package com.example.demo.repository; + +import com.example.demo.model.UserMembership; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface UserMembershipRepository extends JpaRepository { + Optional findByUserIdAndStatus(Long userId, String status); +} diff --git a/demo/src/main/java/com/example/demo/security/PlainTextPasswordEncoder.java b/demo/src/main/java/com/example/demo/security/PlainTextPasswordEncoder.java index 435bef0..07b6528 100644 --- a/demo/src/main/java/com/example/demo/security/PlainTextPasswordEncoder.java +++ b/demo/src/main/java/com/example/demo/security/PlainTextPasswordEncoder.java @@ -25,3 +25,6 @@ public class PlainTextPasswordEncoder implements PasswordEncoder { + + + diff --git a/demo/src/main/resources/data.sql b/demo/src/main/resources/data.sql index 4bcdbf4..1ad57d0 100644 --- a/demo/src/main/resources/data.sql +++ b/demo/src/main/resources/data.sql @@ -1,19 +1,124 @@ --- 示例用户数据 --- 用户名: demo, 密码: demo -INSERT IGNORE INTO users (username, email, password_hash, role, points) VALUES ('demo', 'demo@example.com', 'demo', 'ROLE_USER', 100); +-- 用户数据 +INSERT IGNORE INTO users (username, email, password_hash, role, points, phone, nickname, gender, birthday, address) VALUES +('admin', 'admin@example.com', 'admin123', 'ROLE_ADMIN', 200, '15538239326', '管理员', 'M', '1990-01-01', '北京市朝阳区'), +('demo', 'demo@example.com', 'demo', 'ROLE_USER', 100, '13800138000', '演示用户', 'M', '1995-05-15', '上海市浦东新区'), +('testuser', 'testuser@example.com', 'test123', 'ROLE_USER', 75, '13900139000', '测试用户', 'F', '1992-08-20', '广州市天河区'), +('mingzi_FBx7foZYDS7inLQb', 'mingzi@example.com', '123456', 'ROLE_USER', 25, '13700137000', '名字用户', 'M', '1988-12-10', '深圳市南山区'), +('15538239326', '15538239326@example.com', '0627', 'ROLE_ADMIN', 50, '15538239326', '手机用户', 'F', '1993-03-25', '杭州市西湖区'), +('user001', 'user001@example.com', 'password123', 'ROLE_USER', 150, '13600136000', '用户001', 'M', '1991-07-12', '成都市锦江区'), +('user002', 'user002@example.com', 'password123', 'ROLE_USER', 80, '13500135000', '用户002', 'F', '1994-11-08', '武汉市江汉区'), +('user003', 'user003@example.com', 'password123', 'ROLE_USER', 200, '13400134000', '用户003', 'M', '1989-04-18', '西安市雁塔区'), +('user004', 'user004@example.com', 'password123', 'ROLE_USER', 120, '13300133000', '用户004', 'F', '1996-09-30', '南京市鼓楼区'), +('user005', 'user005@example.com', 'password123', 'ROLE_USER', 90, '13200132000', '用户005', 'M', '1990-06-22', '重庆市渝中区'); --- 用户名: admin, 密码: admin123 -INSERT IGNORE INTO users (username, email, password_hash, role, points) VALUES ('admin', 'admin@example.com', 'admin123', 'ROLE_ADMIN', 200); +-- 会员等级数据 +INSERT IGNORE INTO membership_levels (name, display_name, description, price, duration_days, points_bonus, features) VALUES +('standard', '标准会员', '基础会员服务,包含基本功能', 29.00, 30, 50, '{"video_quality": "720p", "storage": "5GB", "support": "email"}'), +('professional', '专业会员', '专业会员服务,包含高级功能', 99.00, 30, 200, '{"video_quality": "1080p", "storage": "20GB", "support": "priority", "api_access": true}'), +('enterprise', '企业会员', '企业级服务,包含所有功能', 299.00, 30, 500, '{"video_quality": "4K", "storage": "100GB", "support": "dedicated", "api_access": true, "custom_branding": true}'); --- 测试用户1: 用户名: testuser, 密码: test123 -INSERT IGNORE INTO users (username, email, password_hash, role, points) VALUES ('testuser', 'testuser@example.com', 'test123', 'ROLE_USER', 75); +-- 用户会员信息 +INSERT IGNORE INTO user_memberships (user_id, membership_level_id, start_date, end_date, status, auto_renew) VALUES +(1, 2, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', true), +(2, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false), +(3, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false), +(4, 2, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', true), +(5, 2, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', true), +(6, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false), +(7, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false), +(8, 2, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', true), +(9, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false), +(10, 1, '2024-01-01 00:00:00', '2025-12-31 23:59:59', 'ACTIVE', false); --- 测试用户2: 用户名: mingzi_FBx7foZYDS7inLQb, 密码: 123456 (对应个人主页的用户名) -INSERT IGNORE INTO users (username, email, password_hash, role, points) VALUES ('mingzi_FBx7foZYDS7inLQb', 'mingzi@example.com', '123456', 'ROLE_USER', 25); +-- 订单数据 +INSERT IGNORE INTO orders (order_number, total_amount, currency, status, order_type, description, user_id, created_at) VALUES +('ORD20240101001', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 1, '2024-01-01 10:00:00'), +('ORD20240101002', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 2, '2024-01-01 11:00:00'), +('ORD20240101003', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 3, '2024-01-01 12:00:00'), +('ORD20240101004', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 4, '2024-01-01 13:00:00'), +('ORD20240101005', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 5, '2024-01-01 14:00:00'), +('ORD20240101006', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 6, '2024-01-01 15:00:00'), +('ORD20240101007', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 7, '2024-01-01 16:00:00'), +('ORD20240101008', 99.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '专业会员订阅', 8, '2024-01-01 17:00:00'), +('ORD20240101009', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 9, '2024-01-01 18:00:00'), +('ORD20240101010', 29.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '标准会员订阅', 10, '2024-01-01 19:00:00'), +('ORD20240102001', 199.00, 'CNY', 'PENDING', 'PRODUCT', '视频生成服务包', 1, '2024-01-02 09:00:00'), +('ORD20240102002', 99.00, 'CNY', 'PROCESSING', 'PRODUCT', '高级视频编辑', 2, '2024-01-02 10:00:00'), +('ORD20240102003', 299.00, 'CNY', 'COMPLETED', 'MEMBERSHIP', '企业会员订阅', 3, '2024-01-02 11:00:00'), +('ORD20240102004', 49.00, 'CNY', 'CANCELLED', 'PRODUCT', '基础视频生成', 4, '2024-01-02 12:00:00'), +('ORD20240102005', 149.00, 'CNY', 'COMPLETED', 'PRODUCT', '专业视频制作', 5, '2024-01-02 13:00:00'); --- 手机号测试用户: 用户名: 15538239326, 密码: 0627 -INSERT IGNORE INTO users (username, email, password_hash, role, points) VALUES ('15538239326', '15538239326@example.com', '0627', 'ROLE_USER', 50); +-- 订单商品数据 +INSERT IGNORE INTO order_items (product_name, product_description, unit_price, quantity, subtotal, order_id) VALUES +('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 1), +('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 2), +('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 3), +('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 4), +('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 5), +('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 6), +('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 7), +('专业会员订阅', '30天专业会员服务', 99.00, 1, 99.00, 8), +('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 9), +('标准会员订阅', '30天标准会员服务', 29.00, 1, 29.00, 10), +('视频生成服务包', '包含10次视频生成', 199.00, 1, 199.00, 11), +('高级视频编辑', '专业级视频编辑服务', 99.00, 1, 99.00, 12), +('企业会员订阅', '30天企业会员服务', 299.00, 1, 299.00, 13), +('基础视频生成', '单次视频生成服务', 49.00, 1, 49.00, 14), +('专业视频制作', '定制化视频制作', 149.00, 1, 149.00, 15); --- 更新现有用户的积分(如果还没有设置) -UPDATE users SET points = 50 WHERE points IS NULL OR points = 0; +-- 支付数据 +INSERT IGNORE INTO payments (order_id, amount, currency, payment_method, status, description, user_id, created_at, paid_at) VALUES +('ORD20240101001', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 1, '2024-01-01 10:00:00', '2024-01-01 10:05:00'), +('ORD20240101002', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 2, '2024-01-01 11:00:00', '2024-01-01 11:02:00'), +('ORD20240101003', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 3, '2024-01-01 12:00:00', '2024-01-01 12:03:00'), +('ORD20240101004', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 4, '2024-01-01 13:00:00', '2024-01-01 13:04:00'), +('ORD20240101005', 99.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业会员订阅', 5, '2024-01-01 14:00:00', '2024-01-01 14:05:00'), +('ORD20240101006', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 6, '2024-01-01 15:00:00', '2024-01-01 15:02:00'), +('ORD20240101007', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 7, '2024-01-01 16:00:00', '2024-01-01 16:03:00'), +('ORD20240101008', 99.00, 'CNY', 'WECHAT', 'COMPLETED', '专业会员订阅', 8, '2024-01-01 17:00:00', '2024-01-01 17:04:00'), +('ORD20240101009', 29.00, 'CNY', 'ALIPAY', 'COMPLETED', '标准会员订阅', 9, '2024-01-01 18:00:00', '2024-01-01 18:02:00'), +('ORD20240101010', 29.00, 'CNY', 'WECHAT', 'COMPLETED', '标准会员订阅', 10, '2024-01-01 19:00:00', '2024-01-01 19:03:00'), +('ORD20240102001', 199.00, 'CNY', 'ALIPAY', 'PENDING', '视频生成服务包', 1, '2024-01-02 09:00:00', NULL), +('ORD20240102002', 99.00, 'CNY', 'WECHAT', 'PROCESSING', '高级视频编辑', 2, '2024-01-02 10:00:00', NULL), +('ORD20240102003', 299.00, 'CNY', 'ALIPAY', 'COMPLETED', '企业会员订阅', 3, '2024-01-02 11:00:00', '2024-01-02 11:05:00'), +('ORD20240102004', 49.00, 'CNY', 'WECHAT', 'CANCELLED', '基础视频生成', 4, '2024-01-02 12:00:00', NULL), +('ORD20240102005', 149.00, 'CNY', 'ALIPAY', 'COMPLETED', '专业视频制作', 5, '2024-01-02 13:00:00', '2024-01-02 13:04:00'); +-- 视频生成任务数据 +INSERT IGNORE INTO video_tasks (task_id, user_id, task_type, title, description, input_text, status, progress, created_at, completed_at) VALUES +('TASK20240101001', 1, 'TEXT_TO_VIDEO', '产品介绍视频', '为公司新产品制作的介绍视频', '这是一款革命性的AI产品,能够帮助用户快速生成高质量的视频内容...', 'COMPLETED', 100, '2024-01-01 10:00:00', '2024-01-01 10:30:00'), +('TASK20240101002', 2, 'IMAGE_TO_VIDEO', '风景动画', '将静态风景图片转换为动态视频', NULL, 'COMPLETED', 100, '2024-01-01 11:00:00', '2024-01-01 11:25:00'), +('TASK20240101003', 3, 'STORYBOARD_VIDEO', '故事板视频', '基于故事板创建的视频', '从前有一个小村庄,村民们过着平静的生活...', 'PROCESSING', 75, '2024-01-01 12:00:00', NULL), +('TASK20240101004', 4, 'TEXT_TO_VIDEO', '教育视频', '在线教育课程视频', '今天我们来学习Vue.js的基础知识...', 'COMPLETED', 100, '2024-01-01 13:00:00', '2024-01-01 13:35:00'), +('TASK20240101005', 5, 'IMAGE_TO_VIDEO', '产品展示', '产品图片转视频展示', NULL, 'COMPLETED', 100, '2024-01-01 14:00:00', '2024-01-01 14:20:00'), +('TASK20240101006', 6, 'TEXT_TO_VIDEO', '营销视频', '产品营销推广视频', '限时优惠!现在购买享受8折优惠...', 'PENDING', 0, '2024-01-01 15:00:00', NULL), +('TASK20240101007', 7, 'STORYBOARD_VIDEO', '动画短片', '创意动画短片制作', '在一个遥远的星球上,住着一群可爱的小精灵...', 'COMPLETED', 100, '2024-01-01 16:00:00', '2024-01-01 16:45:00'), +('TASK20240101008', 8, 'TEXT_TO_VIDEO', '技术分享', '技术分享会视频', '今天分享的主题是微服务架构的设计原则...', 'PROCESSING', 60, '2024-01-01 17:00:00', NULL), +('TASK20240101009', 9, 'IMAGE_TO_VIDEO', '艺术创作', '艺术作品动态展示', NULL, 'COMPLETED', 100, '2024-01-01 18:00:00', '2024-01-01 18:15:00'), +('TASK20240101010', 10, 'TEXT_TO_VIDEO', '新闻播报', '新闻播报视频', '今日要闻:科技公司发布最新AI技术...', 'FAILED', 0, '2024-01-01 19:00:00', NULL); + +-- 用户作品数据 +INSERT IGNORE INTO user_works (user_id, title, description, work_type, cover_image, video_url, tags, view_count, like_count, created_at) VALUES +(1, '产品介绍视频', '为公司新产品制作的介绍视频', 'VIDEO', '/images/covers/product_intro.jpg', '/videos/product_intro.mp4', '产品,介绍,商业', 1250, 89, '2024-01-01 10:30:00'), +(2, '风景动画', '将静态风景图片转换为动态视频', 'VIDEO', '/images/covers/landscape.jpg', '/videos/landscape.mp4', '风景,动画,自然', 890, 67, '2024-01-01 11:25:00'), +(3, '故事板视频', '基于故事板创建的视频', 'VIDEO', '/images/covers/storyboard.jpg', '/videos/storyboard.mp4', '故事,创意,动画', 2100, 156, '2024-01-01 12:30:00'), +(4, '教育视频', '在线教育课程视频', 'VIDEO', '/images/covers/education.jpg', '/videos/education.mp4', '教育,课程,学习', 3200, 234, '2024-01-01 13:35:00'), +(5, '产品展示', '产品图片转视频展示', 'VIDEO', '/images/covers/product_show.jpg', '/videos/product_show.mp4', '产品,展示,商业', 1560, 112, '2024-01-01 14:20:00'), +(6, '营销视频', '产品营销推广视频', 'VIDEO', '/images/covers/marketing.jpg', '/videos/marketing.mp4', '营销,推广,商业', 2800, 198, '2024-01-01 15:30:00'), +(7, '动画短片', '创意动画短片制作', 'VIDEO', '/images/covers/animation.jpg', '/videos/animation.mp4', '动画,创意,短片', 4500, 345, '2024-01-01 16:45:00'), +(8, '技术分享', '技术分享会视频', 'VIDEO', '/images/covers/tech_share.jpg', '/videos/tech_share.mp4', '技术,分享,编程', 1800, 134, '2024-01-01 17:30:00'), +(9, '艺术创作', '艺术作品动态展示', 'VIDEO', '/images/covers/art.jpg', '/videos/art.mp4', '艺术,创作,美学', 950, 78, '2024-01-01 18:15:00'), +(10, '新闻播报', '新闻播报视频', 'VIDEO', '/images/covers/news.jpg', '/videos/news.mp4', '新闻,播报,资讯', 1200, 89, '2024-01-01 19:30:00'); + +-- 系统配置数据 +INSERT IGNORE INTO system_configs (config_key, config_value, description, config_type, is_public) VALUES +('site_name', 'AIGC视频生成平台', '网站名称', 'STRING', true), +('site_description', '专业的AI视频生成服务平台', '网站描述', 'STRING', true), +('max_file_size', '100', '最大文件上传大小(MB)', 'NUMBER', false), +('supported_formats', '["mp4", "avi", "mov", "wmv"]', '支持的视频格式', 'JSON', true), +('default_video_quality', '1080p', '默认视频质量', 'STRING', false), +('max_video_duration', '300', '最大视频时长(秒)', 'NUMBER', false), +('api_rate_limit', '100', 'API调用频率限制(次/小时)', 'NUMBER', false), +('maintenance_mode', 'false', '维护模式开关', 'BOOLEAN', false), +('registration_enabled', 'true', '用户注册开关', 'BOOLEAN', true), +('email_verification', 'false', '邮箱验证开关', 'BOOLEAN', false); \ No newline at end of file diff --git a/demo/src/main/resources/schema.sql b/demo/src/main/resources/schema.sql index 1c39212..cc5a363 100644 --- a/demo/src/main/resources/schema.sql +++ b/demo/src/main/resources/schema.sql @@ -5,7 +5,16 @@ CREATE TABLE IF NOT EXISTS users ( password_hash VARCHAR(100) NOT NULL, role VARCHAR(30) NOT NULL DEFAULT 'ROLE_USER', points INT NOT NULL DEFAULT 50, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + phone VARCHAR(20), + avatar VARCHAR(500), + nickname VARCHAR(100), + gender VARCHAR(10), + birthday DATE, + address TEXT, + is_active BOOLEAN NOT NULL DEFAULT TRUE, + last_login_at TIMESTAMP NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS payments ( @@ -62,3 +71,84 @@ CREATE TABLE IF NOT EXISTS order_items ( FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE ); +-- 会员等级表 +CREATE TABLE IF NOT EXISTS membership_levels ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(50) NOT NULL UNIQUE, + display_name VARCHAR(50) NOT NULL, + description TEXT, + price DECIMAL(10,2) NOT NULL DEFAULT 0, + duration_days INT NOT NULL DEFAULT 30, + points_bonus INT NOT NULL DEFAULT 0, + features JSON, + is_active BOOLEAN NOT NULL DEFAULT TRUE, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + +-- 用户会员信息表 +CREATE TABLE IF NOT EXISTS user_memberships ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + user_id BIGINT NOT NULL, + membership_level_id BIGINT NOT NULL, + start_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + end_date TIMESTAMP NOT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', + auto_renew BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (membership_level_id) REFERENCES membership_levels(id), + UNIQUE KEY unique_active_membership (user_id, status) +); + +-- 视频生成任务表 +CREATE TABLE IF NOT EXISTS video_tasks ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + task_id VARCHAR(100) NOT NULL UNIQUE, + user_id BIGINT NOT NULL, + task_type VARCHAR(50) NOT NULL, -- TEXT_TO_VIDEO, IMAGE_TO_VIDEO, STORYBOARD_VIDEO + title VARCHAR(200) NOT NULL, + description TEXT, + input_text TEXT, + input_image_url VARCHAR(500), + output_video_url VARCHAR(500), + status VARCHAR(20) NOT NULL DEFAULT 'PENDING', -- PENDING, PROCESSING, COMPLETED, FAILED + progress INT NOT NULL DEFAULT 0, + error_message TEXT, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + completed_at TIMESTAMP NULL, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); + +-- 用户作品表 +CREATE TABLE IF NOT EXISTS user_works ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + user_id BIGINT NOT NULL, + title VARCHAR(200) NOT NULL, + description TEXT, + work_type VARCHAR(50) NOT NULL, -- VIDEO, IMAGE, STORYBOARD + cover_image VARCHAR(500), + video_url VARCHAR(500), + tags VARCHAR(500), + is_public BOOLEAN NOT NULL DEFAULT TRUE, + view_count INT NOT NULL DEFAULT 0, + like_count INT NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); + +-- 系统配置表 +CREATE TABLE IF NOT EXISTS system_configs ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + config_key VARCHAR(100) NOT NULL UNIQUE, + config_value TEXT, + description VARCHAR(500), + config_type VARCHAR(50) NOT NULL DEFAULT 'STRING', -- STRING, NUMBER, BOOLEAN, JSON + is_public BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + diff --git a/demo/src/main/resources/templates/orders/admin.html b/demo/src/main/resources/templates/orders/admin.html index f65bece..9081dd0 100644 --- a/demo/src/main/resources/templates/orders/admin.html +++ b/demo/src/main/resources/templates/orders/admin.html @@ -561,3 +561,6 @@ + + + diff --git a/demo/src/main/resources/templates/orders/detail.html b/demo/src/main/resources/templates/orders/detail.html index 55136c4..094d811 100644 --- a/demo/src/main/resources/templates/orders/detail.html +++ b/demo/src/main/resources/templates/orders/detail.html @@ -477,3 +477,6 @@ + + + diff --git a/demo/src/main/resources/templates/orders/form.html b/demo/src/main/resources/templates/orders/form.html index 9f3c547..431a70c 100644 --- a/demo/src/main/resources/templates/orders/form.html +++ b/demo/src/main/resources/templates/orders/form.html @@ -516,3 +516,6 @@ + + +