修复WorkDetailActivity和WorkItem - 删除重复方法并添加作者字段
This commit is contained in:
parent
a61c50e455
commit
e08318d92f
46
Log/指令/2026年1月8日任务.md
Normal file
46
Log/指令/2026年1月8日任务.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
我们正在开发直播App的三个新功能,请继续之前的工作:
|
||||
|
||||
【功能1】阅后即焚图片消息
|
||||
- 好友私聊中发送阅后即焚图片
|
||||
- 查看几秒后自动销毁
|
||||
- 需要在Android端和后端实现
|
||||
|
||||
【功能2】作品上热门
|
||||
- 用户付费让作品上热门
|
||||
- 热门作品在首页优先展示
|
||||
- 需要付费档位和热门队列
|
||||
|
||||
【功能3】发布动态 + 附近动态
|
||||
- 用户发布动态(文字+图片+位置)
|
||||
- 查看附近的人的动态
|
||||
- 基于地理位置发现
|
||||
|
||||
当前项目结构:
|
||||
- Android端:android-app/app/src/main/java/com/example/livestreaming/
|
||||
- 后端Java:Zhibo/zhibo-h/crmeb-service/src/main/java/com/zbkj/service/
|
||||
- 后端Controller:Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/
|
||||
|
||||
请根据我指定的功能继续开发。
|
||||
我们正在开发直播App的三个新功能,请继续之前的工作:
|
||||
|
||||
【功能1】阅后即焚图片消息
|
||||
- 好友私聊中发送阅后即焚图片
|
||||
- 查看几秒后自动销毁
|
||||
- 需要在Android端和后端实现
|
||||
|
||||
【功能2】作品上热门
|
||||
- 用户付费让作品上热门
|
||||
- 热门作品在首页优先展示
|
||||
- 需要付费档位和热门队列
|
||||
|
||||
【功能3】发布动态 + 附近动态
|
||||
- 用户发布动态(文字+图片+位置)
|
||||
- 查看附近的人的动态
|
||||
- 基于地理位置发现
|
||||
|
||||
当前项目结构:
|
||||
- Android端:android-app/app/src/main/java/com/example/livestreaming/
|
||||
- 后端Java:Zhibo/zhibo-h/crmeb-service/src/main/java/com/zbkj/service/
|
||||
- 后端Controller:Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/
|
||||
|
||||
请根据我指定的功能继续开发。
|
||||
|
|
@ -194,7 +194,6 @@ public class PublishWorkActivity extends AppCompatActivity {
|
|||
getSupportActionBar().setTitle("编辑作品");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setupToolbar() {
|
||||
setSupportActionBar(binding.toolbar);
|
||||
|
|
|
|||
|
|
@ -804,171 +804,6 @@ public class WorkDetailActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
private void setupActionButton() {
|
||||
// ============================================
|
||||
// TODO: 判断是否是当前用户的作品
|
||||
// ============================================
|
||||
// 判断方式(推荐方式3):
|
||||
// 1. 从WorkItem中获取userId字段,与当前登录用户ID比较(需要从AuthStore获取当前用户ID)
|
||||
// 2. 或调用接口: GET /api/works/{workId}/isOwner
|
||||
// 3. 或从作品详情接口返回的WorkItem中包含isOwner字段(推荐,已在获取详情时返回)
|
||||
//
|
||||
// 如果使用方式3(推荐):
|
||||
// - 在获取作品详情时,后端已经返回isOwner字段
|
||||
// - 直接使用 workItem.getIsOwner() 或从详情接口返回的isOwner字段
|
||||
// - 如果isOwner为true,显示编辑/删除按钮
|
||||
// - 如果isOwner为false或用户未登录,隐藏编辑/删除按钮
|
||||
//
|
||||
// 如果使用方式1:
|
||||
// - 需要从AuthStore获取当前登录用户的userId
|
||||
// - 比较 workItem.getUserId() 与当前用户ID
|
||||
// - 如果相同,显示编辑/删除按钮;否则隐藏
|
||||
//
|
||||
// 如果使用方式2:
|
||||
// 接口路径: GET /api/works/{workId}/isOwner
|
||||
// 请求方法: GET
|
||||
// 请求头:
|
||||
// - Authorization: Bearer {token} (必填,需要登录)
|
||||
// 路径参数:
|
||||
// - workId: String (必填) - 作品ID
|
||||
// 返回数据格式: ApiResponse<IsOwnerResponse>
|
||||
// 后端返回: { "code": 200, "data": { "isOwner": true/false } }
|
||||
//
|
||||
// 目前简化处理,所有作品都显示操作按钮
|
||||
// TODO: 根据isOwner字段控制按钮显示/隐藏
|
||||
|
||||
// 新布局中actionButton是ImageView,不是FloatingActionButton(添加防抖)
|
||||
binding.actionButton.setOnClickListener(new DebounceClickListener() {
|
||||
@Override
|
||||
public void onDebouncedClick(View v) {
|
||||
showActionMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showActionMenu() {
|
||||
// TODO: 判断是否是当前用户的作品
|
||||
boolean isOwner = true; // 简化处理
|
||||
|
||||
if (!isOwner) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] options = {"编辑", "删除"};
|
||||
new AlertDialog.Builder(this)
|
||||
.setItems(options, (dialog, which) -> {
|
||||
if (which == 0) {
|
||||
// 编辑
|
||||
editWork();
|
||||
} else if (which == 1) {
|
||||
// 删除
|
||||
deleteWork();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void editWork() {
|
||||
// ============================================
|
||||
// TODO: 实现编辑作品功能
|
||||
// ============================================
|
||||
// 方案1: 跳转到编辑页面(PublishWorkActivity,传入workItem数据)
|
||||
// 方案2: 在当前页面弹出编辑对话框
|
||||
//
|
||||
// 如果使用方案1(推荐):
|
||||
// 1. 创建EditWorkActivity或复用PublishWorkActivity
|
||||
// 2. 传入workItem数据(通过Intent传递)
|
||||
// 3. 在编辑页面预填充作品信息(标题、描述、图片/视频)
|
||||
// 4. 用户修改后,调用更新接口保存
|
||||
//
|
||||
// 更新作品接口:
|
||||
// 接口路径: PUT /api/works/{workId}
|
||||
// 请求方法: PUT
|
||||
// 请求头:
|
||||
// - Authorization: Bearer {token} (必填,需要登录)
|
||||
// 路径参数:
|
||||
// - workId: String (必填) - 作品ID
|
||||
// 请求体 (multipart/form-data 或 JSON):
|
||||
// - title: String (可选) - 作品标题
|
||||
// - description: String (可选) - 作品描述
|
||||
// - cover: File (可选) - 新的封面图片(如果要更换封面)
|
||||
// - video: File (可选) - 新的视频文件(如果要更换视频,仅视频作品)
|
||||
// - images: File[] (可选) - 新的图片文件数组(如果要更换图片,仅图片作品)
|
||||
// - deletedImageIds: String[] (可选) - 要删除的图片ID列表(图片作品)
|
||||
// 返回数据格式: ApiResponse<WorkItem>
|
||||
//
|
||||
// 前端需要传入的参数:
|
||||
// - workId: String (从workItem.getId()获取)
|
||||
// - title: String (用户修改后的标题)
|
||||
// - description: String (用户修改后的描述,可选)
|
||||
// - cover: File (如果要更换封面)
|
||||
// - video/images: File/File[] (如果要更换媒体文件)
|
||||
// - token: String (必填,从AuthStore获取)
|
||||
//
|
||||
// 实现步骤:
|
||||
// 1. 跳转到编辑页面,传入workItem
|
||||
// 2. 在编辑页面加载作品数据并预填充表单
|
||||
// 3. 用户修改内容后点击保存
|
||||
// 4. 验证输入(标题不能为空等)
|
||||
// 5. 如果有新上传的文件,先上传文件获取URL
|
||||
// 6. 调用 PUT /api/works/{workId} 更新作品信息
|
||||
// 7. 更新成功后,刷新当前页面或返回并刷新列表
|
||||
// 8. 处理错误情况(未登录、无权限、文件上传失败等)
|
||||
//
|
||||
// 注意:
|
||||
// - 只有作品作者才能编辑
|
||||
// - 如果更换媒体文件,需要先上传新文件
|
||||
// - 图片作品可以删除部分图片,需要传递deletedImageIds
|
||||
|
||||
Toast.makeText(this, "编辑功能待实现", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private void deleteWork() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("删除作品")
|
||||
.setMessage("确定要删除这个作品吗?")
|
||||
.setPositiveButton("删除", (dialog, which) -> {
|
||||
// 检查登录状态
|
||||
if (!AuthHelper.requireLogin(this, "删除作品需要登录")) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
long worksId = Long.parseLong(workItem.getId());
|
||||
ApiService apiService = ApiClient.getService(this);
|
||||
Call<ApiResponse<Boolean>> call = apiService.deleteWork(worksId);
|
||||
|
||||
call.enqueue(new retrofit2.Callback<ApiResponse<Boolean>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<Boolean>> call, retrofit2.Response<ApiResponse<Boolean>> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
ApiResponse<Boolean> apiResponse = response.body();
|
||||
if (apiResponse.getCode() == 200 && Boolean.TRUE.equals(apiResponse.getData())) {
|
||||
Toast.makeText(WorkDetailActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
} else {
|
||||
Toast.makeText(WorkDetailActivity.this,
|
||||
apiResponse.getMessage() != null ? apiResponse.getMessage() : "删除失败",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(WorkDetailActivity.this, "删除失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Boolean>> call, Throwable t) {
|
||||
Toast.makeText(WorkDetailActivity.this, "网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} catch (NumberFormatException e) {
|
||||
Toast.makeText(this, "作品ID格式错误", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("取消", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
// 图片ViewPager适配器
|
||||
private static class ImagePagerAdapter extends RecyclerView.Adapter<ImagePagerAdapter.VH> {
|
||||
private final List<Uri> imageUris;
|
||||
|
|
@ -1100,64 +935,6 @@ public class WorkDetailActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将WorksResponse转换为WorkItem
|
||||
*/
|
||||
private WorkItem convertToWorkItem(WorksResponse response) {
|
||||
WorkItem item = new WorkItem();
|
||||
item.setId(String.valueOf(response.getId()));
|
||||
item.setTitle(response.getTitle());
|
||||
item.setDescription(response.getDescription());
|
||||
item.setLikeCount(response.getLikeCount());
|
||||
item.setCollectCount(response.getCollectCount());
|
||||
item.setViewCount(response.getViewCount());
|
||||
// 防止 publishTime 为 null 导致 NullPointerException
|
||||
item.setPublishTime(response.getPublishTime() != null ? response.getPublishTime() : 0L);
|
||||
|
||||
// 设置作品类型
|
||||
if ("VIDEO".equals(response.getType())) {
|
||||
item.setType(WorkItem.WorkType.VIDEO);
|
||||
item.setVideoUrl(response.getVideoUrl());
|
||||
if (!TextUtils.isEmpty(response.getVideoUrl())) {
|
||||
item.setVideoUri(Uri.parse(response.getVideoUrl()));
|
||||
}
|
||||
} else {
|
||||
item.setType(WorkItem.WorkType.IMAGE);
|
||||
item.setImageUrls(response.getImageUrls());
|
||||
if (response.getImageUrls() != null && !response.getImageUrls().isEmpty()) {
|
||||
List<Uri> imageUris = new ArrayList<>();
|
||||
for (String url : response.getImageUrls()) {
|
||||
if (!TextUtils.isEmpty(url)) {
|
||||
imageUris.add(Uri.parse(url));
|
||||
}
|
||||
}
|
||||
item.setImageUris(imageUris);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置封面
|
||||
item.setCoverUrl(response.getCoverUrl());
|
||||
if (!TextUtils.isEmpty(response.getCoverUrl())) {
|
||||
item.setCoverUri(Uri.parse(response.getCoverUrl()));
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
} catch (NumberFormatException e) {
|
||||
android.util.Log.e("WorkDetail", "workId格式错误: " + workId, e);
|
||||
Toast.makeText(this, "作品ID格式错误", Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("WorkDetail", "加载作品详情异常", e);
|
||||
Toast.makeText(this, "加载失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将WorksResponse转换为WorkItem
|
||||
*/
|
||||
|
|
@ -1354,153 +1131,6 @@ public class WorkDetailActivity extends AppCompatActivity {
|
|||
.show();
|
||||
}
|
||||
|
||||
private void setupActionButton() {
|
||||
// 检查是否应该显示菜单
|
||||
boolean showMenu = getIntent().getBooleanExtra(EXTRA_SHOW_MENU, false);
|
||||
if (!showMenu) {
|
||||
binding.actionButton.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否是当前用户的作品
|
||||
String currentUserIdStr = AuthStore.getUserId(this);
|
||||
if (currentUserIdStr == null || workItem == null) {
|
||||
binding.actionButton.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int currentUserId = Integer.parseInt(currentUserIdStr);
|
||||
int authorId = workItem.getAuthorId();
|
||||
|
||||
if (currentUserId != authorId) {
|
||||
// 不是自己的作品,隐藏菜单
|
||||
binding.actionButton.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
binding.actionButton.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
// 是自己的作品且需要显示菜单,设置点击事件
|
||||
binding.actionButton.setVisibility(View.VISIBLE);
|
||||
binding.actionButton.setOnClickListener(new DebounceClickListener() {
|
||||
@Override
|
||||
public void onDebouncedClick(View v) {
|
||||
showActionMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showActionMenu() {
|
||||
// 检查是否是当前用户的作品
|
||||
String currentUserIdStr = AuthStore.getUserId(this);
|
||||
if (currentUserIdStr == null || workItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int currentUserId = Integer.parseInt(currentUserIdStr);
|
||||
int authorId = workItem.getAuthorId();
|
||||
|
||||
if (currentUserId != authorId) {
|
||||
Toast.makeText(this, "只能编辑自己的作品", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] options = {"编辑", "删除"};
|
||||
new AlertDialog.Builder(this)
|
||||
.setItems(options, (dialog, which) -> {
|
||||
if (which == 0) {
|
||||
// 编辑
|
||||
editWork();
|
||||
} else if (which == 1) {
|
||||
// 删除
|
||||
deleteWork();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void editWork() {
|
||||
// 检查登录状态
|
||||
if (!AuthHelper.requireLogin(this, "编辑作品需要登录")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (workItem == null) {
|
||||
Toast.makeText(this, "作品信息不完整", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// 跳转到编辑页面(复用PublishWorkActivity)
|
||||
Intent intent = new Intent(this, PublishWorkActivity.class);
|
||||
intent.putExtra("edit_mode", true);
|
||||
intent.putExtra("work_id", workItem.getId());
|
||||
intent.putExtra("work_title", workItem.getTitle());
|
||||
intent.putExtra("work_description", workItem.getDescription());
|
||||
intent.putExtra("work_type", workItem.getType().name());
|
||||
intent.putExtra("work_cover_url", workItem.getCoverUrl());
|
||||
|
||||
if (workItem.getType() == WorkItem.WorkType.VIDEO) {
|
||||
intent.putExtra("work_video_url", workItem.getVideoUrl());
|
||||
} else if (workItem.getImageUrls() != null) {
|
||||
intent.putStringArrayListExtra("work_image_urls", new ArrayList<>(workItem.getImageUrls()));
|
||||
}
|
||||
|
||||
startActivityForResult(intent, 1001);
|
||||
}
|
||||
|
||||
private void deleteWork() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("删除作品")
|
||||
.setMessage("确定要删除这个作品吗?")
|
||||
.setPositiveButton("删除", (dialog, which) -> {
|
||||
// 检查登录状态
|
||||
if (!AuthHelper.requireLogin(this, "删除作品需要登录")) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
long worksId = Long.parseLong(workItem.getId());
|
||||
ApiService apiService = ApiClient.getService(this);
|
||||
Call<ApiResponse<Boolean>> call = apiService.deleteWork(worksId);
|
||||
|
||||
call.enqueue(new retrofit2.Callback<ApiResponse<Boolean>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<Boolean>> call, retrofit2.Response<ApiResponse<Boolean>> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
ApiResponse<Boolean> apiResponse = response.body();
|
||||
if (apiResponse.getCode() == 200 && Boolean.TRUE.equals(apiResponse.getData())) {
|
||||
Toast.makeText(WorkDetailActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
} else {
|
||||
Toast.makeText(WorkDetailActivity.this,
|
||||
apiResponse.getMessage() != null ? apiResponse.getMessage() : "删除失败",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(WorkDetailActivity.this, "删除失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Boolean>> call, Throwable t) {
|
||||
Toast.makeText(WorkDetailActivity.this, "网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} catch (NumberFormatException e) {
|
||||
Toast.makeText(this, "作品ID格式错误", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("取消", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ public class WorkItem implements Parcelable {
|
|||
private long publishTime;
|
||||
private WorkType type; // 作品类型:图片或视频
|
||||
|
||||
// 作者信息
|
||||
private int authorId;
|
||||
private String authorName;
|
||||
private String authorAvatar;
|
||||
|
||||
// 本地使用的URI(发布时使用)
|
||||
private transient Uri coverUri;
|
||||
private transient Uri videoUri;
|
||||
|
|
@ -171,6 +176,30 @@ public class WorkItem implements Parcelable {
|
|||
this.imageUris = imageUris;
|
||||
}
|
||||
|
||||
public int getAuthorId() {
|
||||
return authorId;
|
||||
}
|
||||
|
||||
public void setAuthorId(int authorId) {
|
||||
this.authorId = authorId;
|
||||
}
|
||||
|
||||
public String getAuthorName() {
|
||||
return authorName;
|
||||
}
|
||||
|
||||
public void setAuthorName(String authorName) {
|
||||
this.authorName = authorName;
|
||||
}
|
||||
|
||||
public String getAuthorAvatar() {
|
||||
return authorAvatar;
|
||||
}
|
||||
|
||||
public void setAuthorAvatar(String authorAvatar) {
|
||||
this.authorAvatar = authorAvatar;
|
||||
}
|
||||
|
||||
// Parcelable implementation
|
||||
protected WorkItem(Parcel in) {
|
||||
id = in.readString();
|
||||
|
|
@ -186,6 +215,9 @@ public class WorkItem implements Parcelable {
|
|||
int typeOrdinal = in.readInt();
|
||||
type = typeOrdinal >= 0 && typeOrdinal < WorkType.values().length
|
||||
? WorkType.values()[typeOrdinal] : WorkType.IMAGE;
|
||||
authorId = in.readInt();
|
||||
authorName = in.readString();
|
||||
authorAvatar = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -201,6 +233,9 @@ public class WorkItem implements Parcelable {
|
|||
dest.writeInt(viewCount);
|
||||
dest.writeLong(publishTime);
|
||||
dest.writeInt(type != null ? type.ordinal() : -1);
|
||||
dest.writeInt(authorId);
|
||||
dest.writeString(authorName);
|
||||
dest.writeString(authorAvatar);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user