task(62a18e54-9143-4263-9927-83f1631aa54c): Add M3 task management module (2.6/2.7 task lists and detail)

S-M3 deliverables: TaskApi (2.6 GetUserPlanGroup / 2.7 GetPlanGroup),
TaskModels, feature/task package (Repository/ViewModel/Workspace/Detail/ListAdapter),
layouts and resources; FeatureMount Tab.TASK mounted; placeholder TaskFragment removed.
CommitOnArchive was stuck after staging; committed by master chat as compensation.
This commit is contained in:
阿猫
2026-09-04 00:42:54 +08:00
parent ddd5009bb1
commit 85a144459e
17 changed files with 1693 additions and 8 deletions

View File

@@ -2,6 +2,7 @@ package com.stec.cmd.core.network
import com.stec.cmd.core.network.api.AuthApi
import com.stec.cmd.core.network.api.ProjectApi
import com.stec.cmd.core.network.api.TaskApi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -78,6 +79,11 @@ object NetworkModule {
@Singleton
fun provideProjectApi(retrofit: Retrofit): ProjectApi = retrofit.create(ProjectApi::class.java)
/** 任务接口M32.6/2.7)。 */
@Provides
@Singleton
fun provideTaskApi(retrofit: Retrofit): TaskApi = retrofit.create(TaskApi::class.java)
/** 仅满足 Retrofit 构造约束;真实地址请求期经 DynamicBaseUrlInterceptor 改写。 */
private const val PLACEHOLDER_BASE_URL = "https://placeholder.stec.invalid/"

View File

@@ -0,0 +1,52 @@
package com.stec.cmd.core.network.api
import com.stec.cmd.core.network.ApiEnvelope
import kotlinx.serialization.json.JsonElement
import retrofit2.http.GET
import retrofit2.http.Query
/**
* 任务接口(接口管理规范 V4.5 · 测试环境 https://jcd.stec.p-q.co
* - 2.6 获取 APP 端任务列表M3-01/03/04/05
* - 2.7 获取项目当前监测计划的任务列表M3-02/03/04/05
*
* 三要素请求头SecretKey/SystemCode/Token由 AuthInterceptor 统一注入。
*
* 契约宽容:文档约定 data 为 {"TaskList":[…]};与项目列表(任务 M2 先例)同理
* 保留形态偏差可能,故以 [JsonElement] 宽容承载,
* 由 app 层 TaskRepository.taskListFromData 归一提取。
*/
interface TaskApi {
/**
* 2.6 APP 端任务列表:一个项目的一个监测计划的一个测组为一条任务。
* [planDate] 必填yyyy-MM-dd[projectId]/[workPointId] 传 null 时
* Retrofit 省略参数(空=本人全部项目当日任务)。
*/
@GET(PATH_GET_USER_PLAN_GROUP)
suspend fun getUserPlanGroup(
@Query(QUERY_PROJECT_ID) projectId: String? = null,
@Query(QUERY_PLAN_DATE) planDate: String,
@Query(QUERY_WORK_POINT_ID) workPointId: String? = null,
): ApiEnvelope<JsonElement>
/**
* 2.7 项目当前监测计划任务列表(离当前时间最近的一个监测计划)。
* [projectId] 传 null 时返回所有「进行中」项目的计划任务。
*/
@GET(PATH_GET_PLAN_GROUP)
suspend fun getPlanGroup(
@Query(QUERY_PROJECT_ID) projectId: String? = null,
): ApiEnvelope<JsonElement>
companion object {
// ---- 接口管理规范 V4.5 · 章节 2.1 所有 API 目录 ----
const val PATH_GET_USER_PLAN_GROUP = "OutWebApi/api/GetUserPlanGroup"
const val PATH_GET_PLAN_GROUP = "OutWebApi/api/GetPlanGroup"
/** 查询参数名2.6/2.7 共用 ProjectIDPlanDate 为 2.6 必填)。 */
const val QUERY_PROJECT_ID = "ProjectID"
const val QUERY_PLAN_DATE = "PlanDate"
const val QUERY_WORK_POINT_ID = "WorkPointID"
}
}

View File

@@ -0,0 +1,51 @@
package com.stec.cmd.core.network.api
import kotlinx.serialization.Serializable
/**
* 任务 DTO接口管理规范 V4.5 · 2.6/2.7 返回值示例,一条任务 = 一个测组)。
*
* 字段名与平台契约一致PascalCase文档未给类型全 `String?` 容错,
* 响应侧配合 Json.ignoreUnknownKeys 容忍字段演进。
* 字段来源差异:[PlanNum] 仅 2.6、[PeriodsNum] 仅 2.7(同为期数)、
* [WorkPointID]/[WorkPointName] 仅 2.6。
*/
@Serializable
data class TaskItem(
/** 项目简称。 */
val SimpleName: String? = null,
/** 任务编号:项目编号-计划期数-测组编号。 */
val TaskCode: String? = null,
/** 项目 ID。 */
val ProjectID: String? = null,
/** 监测计划 ID。 */
val PlanID: String? = null,
/** 期数2.6:计划对应期数值)。 */
val PlanNum: String? = null,
/** 期数2.7 字段名)。 */
val PeriodsNum: String? = null,
/** 施工工况。 */
val Working: String? = null,
/** 类型:常规 | 加测。 */
val Mold: String? = null,
/** 责任人。 */
val DutyName: String? = null,
/** 测组 ID。 */
val GroupID: String? = null,
/** 工点 ID仅 2.6)。 */
val WorkPointID: String? = null,
/** 工点名称(仅 2.6)。 */
val WorkPointName: String? = null,
/** 监测类型:水平位移/竖向位移/测斜/收敛/水位/轴力。 */
val MonitoringType: String? = null,
/** 计划周期开始上一期计划截止时间yyyy-MM-dd HH:mm。 */
val BeginTime: String? = null,
/** 计划周期截止yyyy-MM-dd HH:mm。 */
val EndTime: String? = null,
/** 完成度:本测组已上传点数/测组总点数。 */
val CompleteDegree: String? = null,
) {
/** 归一期数2.6 取 PlanNum2.7 取 PeriodsNum。 */
val period: String?
get() = PlanNum?.takeIf { it.isNotBlank() } ?: PeriodsNum?.takeIf { it.isNotBlank() }
}