Explorar o código

feat: 静态资源同步 + 旧库 4x15→4x10 维度迁移

wangkangyjy hai 1 semana
pai
achega
60d872860f
Modificáronse 33 ficheiros con 66 adicións e 9 borrados
  1. 26 0
      backend/src/main/java/com/yimeng/okr/config/DatabaseMigrationRunner.java
  2. 0 0
      backend/src/main/resources/static/assets/AlignmentView-CwwcNHo9.js
  3. 0 0
      backend/src/main/resources/static/assets/AppShell-BYDIuDgE.js
  4. 0 0
      backend/src/main/resources/static/assets/AppShell-Bg67WolE.js
  5. 0 0
      backend/src/main/resources/static/assets/AppShell-BnF8cS3X.css
  6. 0 0
      backend/src/main/resources/static/assets/DimensionConfigView-Bt3qastT.js
  7. 0 0
      backend/src/main/resources/static/assets/FeedbackView-C87RRqTI.js
  8. 0 0
      backend/src/main/resources/static/assets/HistoryView-CdsRLX75.js
  9. 0 0
      backend/src/main/resources/static/assets/KpiTemplateView-opGPlUwd.js
  10. 0 0
      backend/src/main/resources/static/assets/LogView-DDCzkt9s.js
  11. 0 0
      backend/src/main/resources/static/assets/LoginView-Bj7cveE1.js
  12. 0 1
      backend/src/main/resources/static/assets/MainLayout-BaBnrOZ3.js
  13. 0 0
      backend/src/main/resources/static/assets/MyOkrView-_5kqOwji.js
  14. 0 0
      backend/src/main/resources/static/assets/MyResultView-Qj7Z9qfo.js
  15. 0 0
      backend/src/main/resources/static/assets/NotificationView-BKzUQB4j.js
  16. 1 0
      backend/src/main/resources/static/assets/OkrExecutionView-DL9Wigmd.js
  17. 0 1
      backend/src/main/resources/static/assets/OkrExecutionView-Pok_xz0O.js
  18. 0 0
      backend/src/main/resources/static/assets/OrgView-DlsMGbK8.js
  19. 0 0
      backend/src/main/resources/static/assets/PeriodBanner-Ftwac4Vr.js
  20. 0 0
      backend/src/main/resources/static/assets/PeriodListView-BEt1Iye4.js
  21. 0 0
      backend/src/main/resources/static/assets/ScoreAppealView-GPtwh5Lk.js
  22. 0 0
      backend/src/main/resources/static/assets/ScoringEditView-C-fV4BIg.js
  23. 0 0
      backend/src/main/resources/static/assets/ScoringListView-CklJmlAn.js
  24. 1 1
      backend/src/main/resources/static/assets/StatusLabel-DIMzysSV.js
  25. 0 1
      backend/src/main/resources/static/assets/TeamOkrView-Dmttvfz0.js
  26. 0 0
      backend/src/main/resources/static/assets/WorkspaceView-Diqr-FP-.js
  27. 1 1
      backend/src/main/resources/static/assets/auth-C2DTqI-x.js
  28. 1 1
      backend/src/main/resources/static/assets/client-BPGzd84W.js
  29. 1 1
      backend/src/main/resources/static/assets/index-BXvKm1hc.js
  30. 0 0
      backend/src/main/resources/static/assets/index-DWBOIXoA.js
  31. 1 1
      backend/src/main/resources/static/assets/workspace-UhdLcDY9.js
  32. 1 1
      backend/src/main/resources/static/index.html
  33. 33 0
      backend/src/test/java/com/yimeng/okr/config/DatabaseMigrationRunnerTest.java

+ 26 - 0
backend/src/main/java/com/yimeng/okr/config/DatabaseMigrationRunner.java

@@ -205,6 +205,28 @@ public class DatabaseMigrationRunner implements CommandLineRunner {
                 log.warn("No scores could be backfilled from {} rows with comments_json", rows.size());
             }
             log.info("Migration 3 backfill: {} scores processed, {} skipped", backfilled, skipped);
+        }),
+
+        new DatabaseMigration(4, "normalize legacy 4x15 dimensions to 4x10 for OKR 60/KPI 40", context -> {
+            if (!context.tableExists("assessment_dimension")) return;
+            var dims = context.query(
+                    "SELECT id, name, max_score FROM assessment_dimension WHERE deleted = 0 ORDER BY sort_order");
+            if (dims.size() == 4) {
+                boolean all15 = dims.stream().allMatch(
+                        d -> ((Number) d.get("max_score")).intValue() == 15);
+                if (all15) {
+                    for (var dim : dims) {
+                        context.update("UPDATE assessment_dimension SET max_score = 10 WHERE id = ?",
+                                dim.get("id"));
+                    }
+                    log.info("Migration 4: Updated default dimensions from 4x15 to 4x10");
+                }
+            }
+            Integer total = context.queryForInt(
+                    "SELECT COALESCE(SUM(max_score),0) FROM assessment_dimension WHERE deleted = 0");
+            if (total != null && total != 40 && total != 0) {
+                log.warn("Migration 4: Dimension total is {} (expected 40). Manual review may be needed.", total);
+            }
         }));
     }
 
@@ -229,6 +251,10 @@ public class DatabaseMigrationRunner implements CommandLineRunner {
             return jdbcTemplate.queryForList(sql, params);
         }
 
+        Integer queryForInt(String sql, Object... params) {
+            return jdbcTemplate.queryForObject(sql, Integer.class, params);
+        }
+
         int update(String sql, Object... params) {
             return jdbcTemplate.update(sql, params);
         }

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/AlignmentView-CwwcNHo9.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/AppShell-BYDIuDgE.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/AppShell-Bg67WolE.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/AppShell-BnF8cS3X.css


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/DimensionConfigView-Bt3qastT.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/FeedbackView-C87RRqTI.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/HistoryView-CdsRLX75.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/KpiTemplateView-opGPlUwd.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/LogView-DDCzkt9s.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/LoginView-Bj7cveE1.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 1
backend/src/main/resources/static/assets/MainLayout-BaBnrOZ3.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/MyOkrView-_5kqOwji.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/MyResultView-Qj7Z9qfo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/NotificationView-BKzUQB4j.js


+ 1 - 0
backend/src/main/resources/static/assets/OkrExecutionView-DL9Wigmd.js

@@ -0,0 +1 @@
+import{_ as f,E as x,o as n,c as o,a as e,F as y,i as v,G as E,h as b,r as O,t as l,d as i,w as g,b as S}from"./index-BXvKm1hc.js";import{c as w}from"./client-BPGzd84W.js";import{S as T}from"./StatusLabel-DIMzysSV.js";import"./index-B8_nURbH.js";function V(u){return w.get(`/okr/period/${u}/my`)}const R={class:"okr-execution-view"},C={key:0,class:"data-table"},h={style:{"font-weight":"600"}},k={style:{color:"var(--color-text-secondary)"}},D={key:1,class:"empty-state"},L={__name:"OkrExecutionView",setup(u){const c=E(),r=b(null);function p(t){return t.targetValue<=0?0:Math.round(t.currentValue/t.targetValue*100)}function d(t){return{NOT_STARTED:"未开始",IN_PROGRESS:"进行中",COMPLETED:"完成",BLOCKED:"阻塞"}[t]||t}function _(t){return{NOT_STARTED:"info",IN_PROGRESS:"active",COMPLETED:"success",BLOCKED:"danger"}[t]||"info"}return x(async()=>{const t=c.query.periodId;if(t){const a=await V(t);r.value=a.data}}),(t,a)=>{const m=O("el-button");return n(),o("div",R,[a[2]||(a[2]=e("div",{class:"data-toolbar"},[e("span",{class:"data-toolbar__title"},"执行进度")],-1)),r.value?(n(),o("div",C,[a[1]||(a[1]=e("div",{class:"data-table__header",style:{"grid-template-columns":"1fr 80px 80px 100px 80px"}},[e("span",null,"KR"),e("span",null,"进度"),e("span",null,"当前/目标"),e("span",null,"状态"),e("span",null,"操作")],-1)),(n(!0),o(y,null,v(r.value.keyResults,s=>(n(),o("div",{key:s.id,class:"data-table__row",style:{"grid-template-columns":"1fr 80px 80px 100px 80px"}},[e("span",null,l(s.title),1),e("span",h,l(p(s))+"%",1),e("span",k,l(s.currentValue)+"/"+l(s.targetValue),1),i(T,{tone:_(s.status),label:d(s.status)},null,8,["tone","label"]),i(m,{size:"small",onClick:N=>s._showUpdate=!s._showUpdate},{default:g(()=>[...a[0]||(a[0]=[S("更新",-1)])]),_:1},8,["onClick"])]))),128))])):(n(),o("div",D,"加载中..."))])}}},G=f(L,[["__scopeId","data-v-87730cde"]]);export{G as default};

+ 0 - 1
backend/src/main/resources/static/assets/OkrExecutionView-Pok_xz0O.js

@@ -1 +0,0 @@
-import{_ as f,V as x,o as n,c as o,a as e,F as y,i as v,E,h as b,r as O,t as l,d as i,w as g,b as S}from"./index-rPtIq_af.js";import{c as V}from"./client-CEYUV6Me.js";import{S as w}from"./StatusLabel-DFHI8dOe.js";import"./index-B8_nURbH.js";function T(u){return V.get(`/okr/period/${u}/my`)}const R={class:"okr-execution-view"},C={key:0,class:"data-table"},h={style:{"font-weight":"600"}},k={style:{color:"var(--color-text-secondary)"}},D={key:1,class:"empty-state"},L={__name:"OkrExecutionView",setup(u){const c=E(),r=b(null);function p(t){return t.targetValue<=0?0:Math.round(t.currentValue/t.targetValue*100)}function d(t){return{NOT_STARTED:"未开始",IN_PROGRESS:"进行中",COMPLETED:"完成",BLOCKED:"阻塞"}[t]||t}function _(t){return{NOT_STARTED:"info",IN_PROGRESS:"active",COMPLETED:"success",BLOCKED:"danger"}[t]||"info"}return x(async()=>{const t=c.query.periodId;if(t){const a=await T(t);r.value=a.data}}),(t,a)=>{const m=O("el-button");return n(),o("div",R,[a[2]||(a[2]=e("div",{class:"data-toolbar"},[e("span",{class:"data-toolbar__title"},"执行进度")],-1)),r.value?(n(),o("div",C,[a[1]||(a[1]=e("div",{class:"data-table__header",style:{"grid-template-columns":"1fr 80px 80px 100px 80px"}},[e("span",null,"KR"),e("span",null,"进度"),e("span",null,"当前/目标"),e("span",null,"状态"),e("span",null,"操作")],-1)),(n(!0),o(y,null,v(r.value.keyResults,s=>(n(),o("div",{key:s.id,class:"data-table__row",style:{"grid-template-columns":"1fr 80px 80px 100px 80px"}},[e("span",null,l(s.title),1),e("span",h,l(p(s))+"%",1),e("span",k,l(s.currentValue)+"/"+l(s.targetValue),1),i(w,{tone:_(s.status),label:d(s.status)},null,8,["tone","label"]),i(m,{size:"small",onClick:N=>s._showUpdate=!s._showUpdate},{default:g(()=>[...a[0]||(a[0]=[S("更新",-1)])]),_:1},8,["onClick"])]))),128))])):(n(),o("div",D,"加载中..."))])}}},K=f(L,[["__scopeId","data-v-87730cde"]]);export{K as default};

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/OrgView-DlsMGbK8.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/PeriodBanner-Ftwac4Vr.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/PeriodListView-BEt1Iye4.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/ScoreAppealView-GPtwh5Lk.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/ScoringEditView-C-fV4BIg.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/ScoringListView-CklJmlAn.js


+ 1 - 1
backend/src/main/resources/static/assets/StatusLabel-DFHI8dOe.js → backend/src/main/resources/static/assets/StatusLabel-DIMzysSV.js

@@ -1 +1 @@
-import{_ as e,o as l,c as n,a as s,t as o,n as r}from"./index-rPtIq_af.js";const c={class:"status-label__text"},i={__name:"StatusLabel",props:{tone:{type:String,default:"info",validator:a=>["active","success","warning","danger","info"].includes(a)},label:{type:String,required:!0}},setup(a){return(u,t)=>(l(),n("span",{class:r(["status-label",`status-label--${a.tone}`])},[t[0]||(t[0]=s("span",{class:"status-label__dot"},null,-1)),s("span",c,o(a.label),1)],2))}},p=e(i,[["__scopeId","data-v-5856a2c1"]]);export{p as S};
+import{_ as e,o as l,c as n,a as s,t as o,n as r}from"./index-BXvKm1hc.js";const c={class:"status-label__text"},i={__name:"StatusLabel",props:{tone:{type:String,default:"info",validator:a=>["active","success","warning","danger","info"].includes(a)},label:{type:String,required:!0}},setup(a){return(u,t)=>(l(),n("span",{class:r(["status-label",`status-label--${a.tone}`])},[t[0]||(t[0]=s("span",{class:"status-label__dot"},null,-1)),s("span",c,o(a.label),1)],2))}},p=e(i,[["__scopeId","data-v-5856a2c1"]]);export{p as S};

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 1
backend/src/main/resources/static/assets/TeamOkrView-Dmttvfz0.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/WorkspaceView-Diqr-FP-.js


+ 1 - 1
backend/src/main/resources/static/assets/auth-CcY2E36J.js → backend/src/main/resources/static/assets/auth-C2DTqI-x.js

@@ -1 +1 @@
-import{W as t}from"./index-rPtIq_af.js";import{authApi as r}from"./index-CEwiEZa9.js";const n=t("auth",{state:()=>({user:null,accessToken:localStorage.getItem("accessToken")||"",refreshToken:localStorage.getItem("refreshToken")||""}),getters:{isLoggedIn:e=>!!e.accessToken,isAdmin:e=>{var s;return((s=e.user)==null?void 0:s.role)==="SUPER_ADMIN"},isHrAdminOrAbove:e=>{var s;return["SUPER_ADMIN","HR_ADMIN"].includes((s=e.user)==null?void 0:s.role)},isDeptLeaderOrAbove:e=>{var s;return["SUPER_ADMIN","HR_ADMIN","DEPT_LEADER"].includes((s=e.user)==null?void 0:s.role)},roleLabel:e=>{var o;return{EMPLOYEE:"员工",TEAM_LEADER:"团队主管",DEPT_LEADER:"部门负责人",HR_ADMIN:"HR管理员",SUPER_ADMIN:"管理员"}[(o=e.user)==null?void 0:o.role]||"员工"},roleColor:e=>{var o;return{SUPER_ADMIN:"#f56c6c",HR_ADMIN:"#e6a23c",DEPT_LEADER:"#8b5cf6",TEAM_LEADER:"#3ad4d8",EMPLOYEE:"#409eff"}[(o=e.user)==null?void 0:o.role]||"#409eff"}},actions:{async login(e,s){const o=await r.login({username:e,password:s});this.accessToken=o.accessToken,this.refreshToken=o.refreshToken,this.user=o.user,localStorage.setItem("accessToken",o.accessToken),localStorage.setItem("refreshToken",o.refreshToken)},async fetchUser(){try{this.user=await r.me()}catch{this.logout()}},logout(){this.user=null,this.accessToken="",this.refreshToken="",localStorage.removeItem("accessToken"),localStorage.removeItem("refreshToken")}}});export{n as u};
+import{W as t}from"./index-BXvKm1hc.js";import{authApi as r}from"./index-DWBOIXoA.js";const n=t("auth",{state:()=>({user:null,accessToken:localStorage.getItem("accessToken")||"",refreshToken:localStorage.getItem("refreshToken")||""}),getters:{isLoggedIn:e=>!!e.accessToken,isAdmin:e=>{var s;return((s=e.user)==null?void 0:s.role)==="SUPER_ADMIN"},isHrAdminOrAbove:e=>{var s;return["SUPER_ADMIN","HR_ADMIN"].includes((s=e.user)==null?void 0:s.role)},isDeptLeaderOrAbove:e=>{var s;return["SUPER_ADMIN","HR_ADMIN","DEPT_LEADER"].includes((s=e.user)==null?void 0:s.role)},roleLabel:e=>{var o;return{EMPLOYEE:"员工",TEAM_LEADER:"团队主管",DEPT_LEADER:"部门负责人",HR_ADMIN:"HR管理员",SUPER_ADMIN:"管理员"}[(o=e.user)==null?void 0:o.role]||"员工"},roleColor:e=>{var o;return{SUPER_ADMIN:"#f56c6c",HR_ADMIN:"#e6a23c",DEPT_LEADER:"#8b5cf6",TEAM_LEADER:"#3ad4d8",EMPLOYEE:"#409eff"}[(o=e.user)==null?void 0:o.role]||"#409eff"}},actions:{async login(e,s){const o=await r.login({username:e,password:s});this.accessToken=o.accessToken,this.refreshToken=o.refreshToken,this.user=o.user,localStorage.setItem("accessToken",o.accessToken),localStorage.setItem("refreshToken",o.refreshToken)},async fetchUser(){try{this.user=await r.me()}catch{this.logout()}},logout(){this.user=null,this.accessToken="",this.refreshToken="",localStorage.removeItem("accessToken"),localStorage.removeItem("refreshToken")}}});export{n as u};

+ 1 - 1
backend/src/main/resources/static/assets/client-CEYUV6Me.js → backend/src/main/resources/static/assets/client-BPGzd84W.js

@@ -1 +1 @@
-import{a as s}from"./index-B8_nURbH.js";import{$ as n}from"./index-rPtIq_af.js";const o=s.create({baseURL:"/api",timeout:15e3,headers:{"Content-Type":"application/json"}});o.interceptors.request.use(t=>{const e=localStorage.getItem("accessToken");return e&&(t.headers.Authorization=`Bearer ${e}`),t});o.interceptors.response.use(t=>{const{data:e}=t;return e&&e.code!==void 0?e.code===200?e:e.code===401?(localStorage.removeItem("accessToken"),window.location.hash="#/login",Promise.reject(new Error("登录已过期"))):Promise.reject({...e,_handled:!0}):e},t=>{var r;const{response:e}=t;return e&&e.status===409?Promise.reject({code:409,message:"数据已被他人修改,请刷新后重试",conflictVersion:(r=e.data)==null?void 0:r.conflictVersion,_handled:!0}):(t._handled||n.error(t.message||"请求失败"),Promise.reject(t))});export{o as c};
+import{a as s}from"./index-B8_nURbH.js";import{$ as n}from"./index-BXvKm1hc.js";const o=s.create({baseURL:"/api",timeout:15e3,headers:{"Content-Type":"application/json"}});o.interceptors.request.use(t=>{const e=localStorage.getItem("accessToken");return e&&(t.headers.Authorization=`Bearer ${e}`),t});o.interceptors.response.use(t=>{const{data:e}=t;return e&&e.code!==void 0?e.code===200?e:e.code===401?(localStorage.removeItem("accessToken"),window.location.hash="#/login",Promise.reject(new Error("登录已过期"))):Promise.reject({...e,_handled:!0}):e},t=>{var r;const{response:e}=t;return e&&e.status===409?Promise.reject({code:409,message:"数据已被他人修改,请刷新后重试",conflictVersion:(r=e.data)==null?void 0:r.conflictVersion,_handled:!0}):(t._handled||n.error(t.message||"请求失败"),Promise.reject(t))});export{o as c};

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
backend/src/main/resources/static/assets/index-BXvKm1hc.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
backend/src/main/resources/static/assets/index-DWBOIXoA.js


+ 1 - 1
backend/src/main/resources/static/assets/workspace-B3hIjoAi.js → backend/src/main/resources/static/assets/workspace-UhdLcDY9.js

@@ -1 +1 @@
-import{W as g,h as r,C as y,u as k,E as f}from"./index-rPtIq_af.js";import{c as q}from"./client-CEYUV6Me.js";function S(a){const s=a?{periodId:a}:{};return q.get("/workspace/overview",{params:s})}const A=g("workspace",()=>{const a=r(null),s=r([]),c=r(null),l=r([]),i=r([]),d=r(null),v=r([]),m=r([]),n=r(!1),u=r(null),w=y(()=>!!a.value),h=y(()=>{var o;try{const t=f();if(t.query.periodId)return Number(t.query.periodId)}catch{}return((o=a.value)==null?void 0:o.id)||null});async function p(o){n.value=!0,u.value=null;try{const e=(await S(o)).data;a.value=e.recommendedPeriod,s.value=e.availablePeriods||[],c.value=e.currentPhase,l.value=e.myTodos||[],i.value=e.allowedActions||[],d.value=e.teamSummary,v.value=e.risks||[],m.value=e.upcomingDeadlines||[]}catch(t){u.value=t.message||"加载工作台失败"}finally{n.value=!1}}async function P(o){try{const t=k(),e=f();t.replace({query:{...e.query,periodId:o}})}catch{}await p(o)}return{currentPeriod:a,availablePeriods:s,currentPhase:c,todos:l,allowedActions:i,teamSummary:d,risks:v,deadlines:m,loading:n,error:u,hasPeriod:w,periodId:h,refreshOverview:p,selectPeriod:P}});export{A as u};
+import{W as g,h as r,C as y,u as k,G as f}from"./index-BXvKm1hc.js";import{c as q}from"./client-BPGzd84W.js";function S(a){const s=a?{periodId:a}:{};return q.get("/workspace/overview",{params:s})}const A=g("workspace",()=>{const a=r(null),s=r([]),c=r(null),l=r([]),i=r([]),d=r(null),v=r([]),m=r([]),n=r(!1),u=r(null),w=y(()=>!!a.value),h=y(()=>{var o;try{const t=f();if(t.query.periodId)return Number(t.query.periodId)}catch{}return((o=a.value)==null?void 0:o.id)||null});async function p(o){n.value=!0,u.value=null;try{const e=(await S(o)).data;a.value=e.recommendedPeriod,s.value=e.availablePeriods||[],c.value=e.currentPhase,l.value=e.myTodos||[],i.value=e.allowedActions||[],d.value=e.teamSummary,v.value=e.risks||[],m.value=e.upcomingDeadlines||[]}catch(t){u.value=t.message||"加载工作台失败"}finally{n.value=!1}}async function P(o){try{const t=k(),e=f();t.replace({query:{...e.query,periodId:o}})}catch{}await p(o)}return{currentPeriod:a,availablePeriods:s,currentPhase:c,todos:l,allowedActions:i,teamSummary:d,risks:v,deadlines:m,loading:n,error:u,hasPeriod:w,periodId:h,refreshOverview:p,selectPeriod:P}});export{A as u};

+ 1 - 1
backend/src/main/resources/static/index.html

@@ -5,7 +5,7 @@
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>医梦AI · OKR绩效</title>
   <link rel="icon" type="image/png" href="/logo-small.png" />
-  <script type="module" crossorigin src="/assets/index-rPtIq_af.js"></script>
+  <script type="module" crossorigin src="/assets/index-BXvKm1hc.js"></script>
   <link rel="stylesheet" crossorigin href="/assets/index-DngTeEPG.css">
 </head>
 <body>

+ 33 - 0
backend/src/test/java/com/yimeng/okr/config/DatabaseMigrationRunnerTest.java

@@ -222,6 +222,39 @@ class DatabaseMigrationRunnerTest {
         assertTrue(exists, tableName + "." + columnName + " should exist");
     }
 
+
+    @Test
+    void shouldMigrateLegacy4x15DimensionsTo4x10() {
+        createBaseTables();
+        jdbcTemplate.execute("""
+                CREATE TABLE assessment_dimension (
+                    id INTEGER PRIMARY KEY AUTOINCREMENT,
+                    name VARCHAR(64) NOT NULL,
+                    max_score INTEGER NOT NULL DEFAULT 10,
+                    sort_order INTEGER DEFAULT 0,
+                    deleted INTEGER NOT NULL DEFAULT 0,
+                    created_at TEXT NOT NULL,
+                    updated_at TEXT NOT NULL
+                )
+                """);
+        jdbcTemplate.update("INSERT INTO assessment_dimension(id,name,max_score,sort_order,created_at,updated_at) VALUES (1,\"交付质量\",15,1,datetime(\"now\"),datetime(\"now\"))");
+        jdbcTemplate.update("INSERT INTO assessment_dimension(id,name,max_score,sort_order,created_at,updated_at) VALUES (2,\"协作沟通\",15,2,datetime(\"now\"),datetime(\"now\"))");
+        jdbcTemplate.update("INSERT INTO assessment_dimension(id,name,max_score,sort_order,created_at,updated_at) VALUES (3,\"学习成长\",15,3,datetime(\"now\"),datetime(\"now\"))");
+        jdbcTemplate.update("INSERT INTO assessment_dimension(id,name,max_score,sort_order,created_at,updated_at) VALUES (4,\"创新贡献\",15,4,datetime(\"now\"),datetime(\"now\"))");
+
+        DatabaseMigrationRunner runner = new DatabaseMigrationRunner(jdbcTemplate, transactionTemplate, backupService);
+        runner.run();
+
+        for (int i = 1; i <= 4; i++) {
+            int maxScore = jdbcTemplate.queryForObject(
+                "SELECT max_score FROM assessment_dimension WHERE id = " + i, Integer.class);
+            assertEquals(10, maxScore, "Dim " + i + " should be 10");
+        }
+
+        assertEquals(1, jdbcTemplate.queryForObject(
+            "SELECT COUNT(*) FROM schema_migration WHERE version = 4", Integer.class));
+    }
+
     private void assertTableExists(JdbcTemplate jdbcTemplate, String tableName) {
         Integer count = jdbcTemplate.queryForObject("""
                 SELECT COUNT(*) FROM sqlite_master

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio